/*
 *
 * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc.
 *             2005 Lars Knoll & Zack Rusin, Trolltech
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Keith Packard not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission.  Keith Packard makes no
 * representations about the suitability of this software for any purpose.  It
 * is provided "as is" without express or implied warranty.
 *
 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 */

#ifdef HAVE_DIX_CONFIG_H
#include <dix-config.h>
#endif

#include "fb.h"


#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <assert.h>

#include "picturestr.h"
#include "mipict.h"
#include "fbpict.h"

#define mod(a,b)	((b) == 1 ? 0 : (a) >= 0 ? (a) % (b) : (b) - (-a) % (b))

#define SCANLINE_BUFFER_LENGTH 2048

typedef FASTCALL void (*fetchProc) (const FbBits * bits, int x, int width,
                                    CARD32 *buffer, miIndexedPtr indexed);

/*
 * All of the fetch functions
 */

static FASTCALL void
fbFetch_a8r8g8b8(const FbBits * bits, int x, int width, CARD32 *buffer,
                 miIndexedPtr indexed)
{
    memcpy(buffer, (const CARD32 *) bits + x, width * sizeof(CARD32));
}

static FASTCALL void
fbFetch_x8r8g8b8(const FbBits * bits, int x, int width, CARD32 *buffer,
                 miIndexedPtr indexed)
{
    const CARD32 *pixel = (const CARD32 *) bits + x;

    const CARD32 *end = pixel + width;

    while (pixel < end) {
        *buffer++ = *pixel++ | 0xff000000;
    }
}

static FASTCALL void
fbFetch_a8b8g8r8(const FbBits * bits, int x, int width, CARD32 *buffer,
                 miIndexedPtr indexed)
{
    const CARD32 *pixel = (CARD32 *) bits + x;

    const CARD32 *end = pixel + width;

    while (pixel < end) {
        *buffer++ = ((*pixel & 0xff00ff00) |
                     ((*pixel >> 16) & 0xff) | ((*pixel & 0xff) << 16));
        ++pixel;
    }
}

static FASTCALL void
fbFetch_x8b8g8r8(const FbBits * bits, int x, int width, CARD32 *buffer,
                 miIndexedPtr indexed)
{
    const CARD32 *pixel = (CARD32 *) bits + x;

    const CARD32 *end = pixel + width;

    while (pixel < end) {
        *buffer++ = 0xff000000 |
            ((*pixel & 0x0000ff00) |
             ((*pixel >> 16) & 0xff) | ((*pixel & 0xff) << 16));
        ++pixel;
    }
}

static FASTCALL void
fbFetch_r8g8b8(const FbBits * bits, int x, int width, CARD32 *buffer,
               miIndexedPtr indexed)
{
    const CARD8 *pixel = (const CARD8 *) bits + 3 * x;

    const CARD8 *end = pixel + 3 * width;

    while (pixel < end) {
        CARD32 b = Fetch24(pixel) | 0xff000000;

        pixel += 3;
        *buffer++ = b;
    }
}

static FASTCALL void
fbFetch_b8g8r8(const FbBits * bits, int x, int width, CARD32 *buffer,
               miIndexedPtr indexed)
{
    const CARD8 *pixel = (const CARD8 *) bits + 3 * x;

    const CARD8 *end = pixel + 3 * width;

    while (pixel < end) {
        CARD32 b = 0xff000000;

#if IMAGE_BYTE_ORDER == MSBFirst
        b |= (*pixel++);
        b |= (*pixel++ << 8);
        b |= (*pixel++ << 16);
#else
        b |= (*pixel++ << 16);
        b |= (*pixel++ << 8);
        b |= (*pixel++);
#endif
        *buffer++ = b;
    }
}

static FASTCALL void
fbFetch_r5g6b5(const FbBits * bits, int x, int width, CARD32 *buffer,
               miIndexedPtr indexed)
{
    const CARD16 *pixel = (const CARD16 *) bits + x;

    const CARD16 *end = pixel + width;

    while (pixel < end) {
        CARD32 p = *pixel++;

        CARD32 r = (((p) << 3) & 0xf8) |
            (((p) << 5) & 0xfc00) | (((p) << 8) & 0xf80000);
        r |= (r >> 5) & 0x70007;
        r |= (r >> 6) & 0x300;
        *buffer++ = 0xff000000 | r;
    }
}

static FASTCALL void
fbFetch_b5g6r5(const FbBits * bits, int x, int width, CARD32 *buffer,
               miIndexedPtr indexed)
{
    const CARD16 *pixel = (const CARD16 *) bits + x;

    const CARD16 *end = pixel + width;

    while (pixel < end) {
        CARD32 p = *pixel++;

        CARD32 r, g, b;

        b = ((p & 0xf800) | ((p & 0xe000) >> 5)) >> 8;
        g = ((p & 0x07e0) | ((p & 0x0600) >> 6)) << 5;
        r = ((p & 0x001c) | ((p & 0x001f) << 5)) << 14;
        *buffer++ = (0xff000000 | r | g | b);
    }
}

static FASTCALL void
fbFetch_a1r5g5b5(const FbBits * bits, int x, int width, CARD32 *buffer,
                 miIndexedPtr indexed)
{
    const CARD16 *pixel = (const CARD16 *) bits + x;

    const CARD16 *end = pixel + width;

    while (pixel < end) {
        CARD32 p = *pixel++;

        CARD32 r, g, b, a;

        a = (CARD32) ((CARD8) (0 - ((p & 0x8000) >> 15))) << 24;
        r = ((p & 0x7c00) | ((p & 0x7000) >> 5)) << 9;
        g = ((p & 0x03e0) | ((p & 0x0380) >> 5)) << 6;
        b = ((p & 0x001c) | ((p & 0x001f) << 5)) >> 2;
        *buffer++ = (a | r | g | b);
    }
}

static FASTCALL void
fbFetch_x1r5g5b5(const FbBits * bits, int x, int width, CARD32 *buffer,
                 miIndexedPtr indexed)
{
    const CARD16 *pixel = (const CARD16 *) bits + x;

    const CARD16 *end = pixel + width;

    while (pixel < end) {
        CARD32 p = *pixel++;

        CARD32 r, g, b;

        r = ((p & 0x7c00) | ((p & 0x7000) >> 5)) << 9;
        g = ((p & 0x03e0) | ((p & 0x0380) >> 5)) << 6;
        b = ((p & 0x001c) | ((p & 0x001f) << 5)) >> 2;
        *buffer++ = (0xff000000 | r | g | b);
    }
}

static FASTCALL void
fbFetch_a1b5g5r5(const FbBits * bits, int x, int width, CARD32 *buffer,
                 miIndexedPtr indexed)
{
    const CARD16 *pixel = (const CARD16 *) bits + x;

    const CARD16 *end = pixel + width;

    while (pixel < end) {
        CARD32 p = *pixel++;

        CARD32 r, g, b, a;

        a = (CARD32) ((CARD8) (0 - ((p & 0x8000) >> 15))) << 24;
        b = ((p & 0x7c00) | ((p & 0x7000) >> 5)) >> 7;
        g = ((p & 0x03e0) | ((p & 0x0380) >> 5)) << 6;
        r = ((p & 0x001c) | ((p & 0x001f) << 5)) << 14;
        *buffer++ = (a | r | g | b);
    }
}

static FASTCALL void
fbFetch_x1b5g5r5(const FbBits * bits, int x, int width, CARD32 *buffer,
                 miIndexedPtr indexed)
{
    const CARD16 *pixel = (const CARD16 *) bits + x;

    const CARD16 *end = pixel + width;

    while (pixel < end) {
        CARD32 p = *pixel++;

        CARD32 r, g, b;

        b = ((p & 0x7c00) | ((p & 0x7000) >> 5)) >> 7;
        g = ((p & 0x03e0) | ((p & 0x0380) >> 5)) << 6;
        r = ((p & 0x001c) | ((p & 0x001f) << 5)) << 14;
        *buffer++ = (0xff000000 | r | g | b);
    }
}

static FASTCALL void
fbFetch_a4r4g4b4(const FbBits * bits, int x, int width, CARD32 *buffer,
                 miIndexedPtr indexed)
{
    const CARD16 *pixel = (const CARD16 *) bits + x;

    const CARD16 *end = pixel + width;

    while (pixel < end) {
        CARD32 p = *pixel++;

        CARD32 r, g, b, a;

        a = ((p & 0xf000) | ((p & 0xf000) >> 4)) << 16;
        r = ((p & 0x0f00) | ((p & 0x0f00) >> 4)) << 12;
        g = ((p & 0x00f0) | ((p & 0x00f0) >> 4)) << 8;
        b = ((p & 0x000f) | ((p & 0x000f) << 4));
        *buffer++ = (a | r | g | b);
    }
}

static FASTCALL void
fbFetch_x4r4g4b4(const FbBits * bits, int x, int width, CARD32 *buffer,
                 miIndexedPtr indexed)
{
    const CARD16 *pixel = (const CARD16 *) bits + x;

    const CARD16 *end = pixel + width;

    while (pixel < end) {
        CARD32 p = *pixel++;

        CARD32 r, g, b;

        r = ((p & 0x0f00) | ((p & 0x0f00) >> 4)) << 12;
        g = ((p & 0x00f0) | ((p & 0x00f0) >> 4)) << 8;
        b = ((p & 0x000f) | ((p & 0x000f) << 4));
        *buffer++ = (0xff000000 | r | g | b);
    }
}

static FASTCALL void
fbFetch_a4b4g4r4(const FbBits * bits, int x, int width, CARD32 *buffer,
                 miIndexedPtr indexed)
{
    const CARD16 *pixel = (const CARD16 *) bits + x;

    const CARD16 *end = pixel + width;

    while (pixel < end) {
        CARD32 p = *pixel++;

        CARD32 r, g, b, a;

        a = ((p & 0xf000) | ((p & 0xf000) >> 4)) << 16;
        b = ((p & 0x0f00) | ((p & 0x0f00) >> 4)) >> 4;
        g = ((p & 0x00f0) | ((p & 0x00f0) >> 4)) << 8;
        r = ((p & 0x000f) | ((p & 0x000f) << 4)) << 16;
        *buffer++ = (a | r | g | b);
    }
}

static FASTCALL void
fbFetch_x4b4g4r4(const FbBits * bits, int x, int width, CARD32 *buffer,
                 miIndexedPtr indexed)
{
    const CARD16 *pixel = (const CARD16 *) bits + x;

    const CARD16 *end = pixel + width;

    while (pixel < end) {
        CARD32 p = *pixel++;

        CARD32 r, g, b;

        b = ((p & 0x0f00) | ((p & 0x0f00) >> 4)) >> 4;
        g = ((p & 0x00f0) | ((p & 0x00f0) >> 4)) << 8;
        r = ((p & 0x000f) | ((p & 0x000f) << 4)) << 16;
        *buffer++ = (0xff000000 | r | g | b);
    }
}

static FASTCALL void
fbFetch_a8(const FbBits * bits, int x, int width, CARD32 *buffer,
           miIndexedPtr indexed)
{
    const CARD8 *pixel = (const CARD8 *) bits + x;

    const CARD8 *end = pixel + width;

    while (pixel < end) {
        *buffer++ = (*pixel++) << 24;
    }
}

static FASTCALL void
fbFetch_r3g3b2(const FbBits * bits, int x, int width, CARD32 *buffer,
               miIndexedPtr indexed)
{
    const CARD8 *pixel = (const CARD8 *) bits + x;

    const CARD8 *end = pixel + width;

    while (pixel < end) {
        CARD32 p = *pixel++;

        CARD32 r, g, b;

        r = ((p & 0xe0) | ((p & 0xe0) >> 3) | ((p & 0xc0) >> 6)) << 16;
        g = ((p & 0x1c) | ((p & 0x18) >> 3) | ((p & 0x1c) << 3)) << 8;
        b = (((p & 0x03)) |
             ((p & 0x03) << 2) | ((p & 0x03) << 4) | ((p & 0x03) << 6));
        *buffer++ = (0xff000000 | r | g | b);
    }
}

static FASTCALL void
fbFetch_b2g3r3(const FbBits * bits, int x, int width, CARD32 *buffer,
               miIndexedPtr indexed)
{
    const CARD8 *pixel = (const CARD8 *) bits + x;

    const CARD8 *end = pixel + width;

    while (pixel < end) {
        CARD32 p = *pixel++;

        CARD32 r, g, b;

        b = (((p & 0xc0)) |
             ((p & 0xc0) >> 2) | ((p & 0xc0) >> 4) | ((p & 0xc0) >> 6));
        g = ((p & 0x38) | ((p & 0x38) >> 3) | ((p & 0x30) << 2)) << 8;
        r = (((p & 0x07)) | ((p & 0x07) << 3) | ((p & 0x06) << 6)) << 16;
        *buffer++ = (0xff000000 | r | g | b);
    }
}

static FASTCALL void
fbFetch_a2r2g2b2(const FbBits * bits, int x, int width, CARD32 *buffer,
                 miIndexedPtr indexed)
{
    const CARD8 *pixel = (const CARD8 *) bits + x;

    const CARD8 *end = pixel + width;

    while (pixel < end) {
        CARD32 p = *pixel++;

        CARD32 a, r, g, b;

        a = ((p & 0xc0) * 0x55) << 18;
        r = ((p & 0x30) * 0x55) << 12;
        g = ((p & 0x0c) * 0x55) << 6;
        b = ((p & 0x03) * 0x55);
        *buffer++ = a | r | g | b;
    }
}

static FASTCALL void
fbFetch_a2b2g2r2(const FbBits * bits, int x, int width, CARD32 *buffer,
                 miIndexedPtr indexed)
{
    const CARD8 *pixel = (const CARD8 *) bits + x;

    const CARD8 *end = pixel + width;

    while (pixel < end) {
        CARD32 p = *pixel++;

        CARD32 a, r, g, b;

        a = ((p & 0xc0) * 0x55) << 18;
        b = ((p & 0x30) * 0x55) >> 6;
        g = ((p & 0x0c) * 0x55) << 6;
        r = ((p & 0x03) * 0x55) << 16;
        *buffer++ = a | r | g | b;
    }
}

static FASTCALL void
fbFetch_c8(const FbBits * bits, int x, int width, CARD32 *buffer,
           miIndexedPtr indexed)
{
    const CARD8 *pixel = (const CARD8 *) bits + x;

    const CARD8 *end = pixel + width;

    while (pixel < end) {
        CARD32 p = *pixel++;

        *buffer++ = indexed->rgba[p];
    }
}

static FASTCALL void
fbFetch_x4a4(const FbBits * bits, int x, int width, CARD32 *buffer,
             miIndexedPtr indexed)
{
    const CARD8 *pixel = (const CARD8 *) bits + x;

    const CARD8 *end = pixel + width;

    while (pixel < end) {
        CARD8 p = (*pixel++) & 0xf;

        *buffer++ = (p | (p << 4)) << 24;
    }
}

#define Fetch8(l,o)    (((CARD8 *) (l))[(o) >> 2])
#if IMAGE_BYTE_ORDER == MSBFirst
#define Fetch4(l,o)    ((o) & 2 ? Fetch8(l,o) & 0xf : Fetch8(l,o) >> 4)
#else
#define Fetch4(l,o)    ((o) & 2 ? Fetch8(l,o) >> 4 : Fetch8(l,o) & 0xf)
#endif

static FASTCALL void
fbFetch_a4(const FbBits * bits, int x, int width, CARD32 *buffer,
           miIndexedPtr indexed)
{
    int i;

    for (i = 0; i < width; ++i) {
        CARD32 p = Fetch4(bits, i + x);

        p |= p << 4;
        *buffer++ = p << 24;
    }
}

static FASTCALL void
fbFetch_r1g2b1(const FbBits * bits, int x, int width, CARD32 *buffer,
               miIndexedPtr indexed)
{
    int i;

    for (i = 0; i < width; ++i) {
        CARD32 p = Fetch4(bits, i + x);

        CARD32 r, g, b;

        r = ((p & 0x8) * 0xff) << 13;
        g = ((p & 0x6) * 0x55) << 7;
        b = ((p & 0x1) * 0xff);
        *buffer++ = 0xff000000 | r | g | b;
    }
}

static FASTCALL void
fbFetch_b1g2r1(const FbBits * bits, int x, int width, CARD32 *buffer,
               miIndexedPtr indexed)
{
    int i;

    for (i = 0; i < width; ++i) {
        CARD32 p = Fetch4(bits, i + x);

        CARD32 r, g, b;

        b = ((p & 0x8) * 0xff) >> 3;
        g = ((p & 0x6) * 0x55) << 7;
        r = ((p & 0x1) * 0xff) << 16;
        *buffer++ = 0xff000000 | r | g | b;
    }
}

static FASTCALL void
fbFetch_a1r1g1b1(const FbBits * bits, int x, int width, CARD32 *buffer,
                 miIndexedPtr indexed)
{
    int i;

    for (i = 0; i < width; ++i) {
        CARD32 p = Fetch4(bits, i + x);

        CARD32 a, r, g, b;

        a = ((p & 0x8) * 0xff) << 21;
        r = ((p & 0x4) * 0xff) << 14;
        g = ((p & 0x2) * 0xff) << 7;
        b = ((p & 0x1) * 0xff);
        *buffer++ = a | r | g | b;
    }
}

static FASTCALL void
fbFetch_a1b1g1r1(const FbBits * bits, int x, int width, CARD32 *buffer,
                 miIndexedPtr indexed)
{
    int i;

    for (i = 0; i < width; ++i) {
        CARD32 p = Fetch4(bits, i + x);

        CARD32 a, r, g, b;

        a = ((p & 0x8) * 0xff) << 21;
        r = ((p & 0x4) * 0xff) >> 3;
        g = ((p & 0x2) * 0xff) << 7;
        b = ((p & 0x1) * 0xff) << 16;
        *buffer++ = a | r | g | b;
    }
}

static FASTCALL void
fbFetch_c4(const FbBits * bits, int x, int width, CARD32 *buffer,
           miIndexedPtr indexed)
{
    int i;

    for (i = 0; i < width; ++i) {
        CARD32 p = Fetch4(bits, i + x);

        *buffer++ = indexed->rgba[p];
    }
}

static FASTCALL void
fbFetch_a1(const FbBits * bits, int x, int width, CARD32 *buffer,
           miIndexedPtr indexed)
{
    int i;

    for (i = 0; i < width; ++i) {
        CARD32 p = ((CARD32 *) bits)[(i + x) >> 5];

        CARD32 a;

#if BITMAP_BIT_ORDER == MSBFirst
        a = p >> (0x1f - ((i + x) & 0x1f));
#else
        a = p >> ((i + x) & 0x1f);
#endif
        a = a & 1;
        a |= a << 1;
        a |= a << 2;
        a |= a << 4;
        *buffer++ = a << 24;
    }
}

static FASTCALL void
fbFetch_g1(const FbBits * bits, int x, int width, CARD32 *buffer,
           miIndexedPtr indexed)
{
    int i;

    for (i = 0; i < width; ++i) {
        CARD32 p = ((CARD32 *) bits)[(i + x) >> 5];

        CARD32 a;

#if BITMAP_BIT_ORDER == MSBFirst
        a = p >> (0x1f - ((i + x) & 0x1f));
#else
        a = p >> ((i + x) & 0x1f);
#endif
        a = a & 1;
        *buffer++ = indexed->rgba[a];
    }
}

static fetchProc
fetchProcForPicture(PicturePtr pict)
{
    switch (pict->format) {
    case PICT_a8r8g8b8:
        return fbFetch_a8r8g8b8;
    case PICT_x8r8g8b8:
        return fbFetch_x8r8g8b8;
    case PICT_a8b8g8r8:
        return fbFetch_a8b8g8r8;
    case PICT_x8b8g8r8:
        return fbFetch_x8b8g8r8;

        /* 24bpp formats */
    case PICT_r8g8b8:
        return fbFetch_r8g8b8;
    case PICT_b8g8r8:
        return fbFetch_b8g8r8;

        /* 16bpp formats */
    case PICT_r5g6b5:
        return fbFetch_r5g6b5;
    case PICT_b5g6r5:
        return fbFetch_b5g6r5;

    case PICT_a1r5g5b5:
        return fbFetch_a1r5g5b5;
    case PICT_x1r5g5b5:
        return fbFetch_x1r5g5b5;
    case PICT_a1b5g5r5:
        return fbFetch_a1b5g5r5;
    case PICT_x1b5g5r5:
        return fbFetch_x1b5g5r5;
    case PICT_a4r4g4b4:
        return fbFetch_a4r4g4b4;
    case PICT_x4r4g4b4:
        return fbFetch_x4r4g4b4;
    case PICT_a4b4g4r4:
        return fbFetch_a4b4g4r4;
    case PICT_x4b4g4r4:
        return fbFetch_x4b4g4r4;

        /* 8bpp formats */
    case PICT_a8:
        return fbFetch_a8;
    case PICT_r3g3b2:
        return fbFetch_r3g3b2;
    case PICT_b2g3r3:
        return fbFetch_b2g3r3;
    case PICT_a2r2g2b2:
        return fbFetch_a2r2g2b2;
    case PICT_a2b2g2r2:
        return fbFetch_a2b2g2r2;
    case PICT_c8:
        return fbFetch_c8;
    case PICT_g8:
        return fbFetch_c8;
    case PICT_x4a4:
        return fbFetch_x4a4;

        /* 4bpp formats */
    case PICT_a4:
        return fbFetch_a4;
    case PICT_r1g2b1:
        return fbFetch_r1g2b1;
    case PICT_b1g2r1:
        return fbFetch_b1g2r1;
    case PICT_a1r1g1b1:
        return fbFetch_a1r1g1b1;
    case PICT_a1b1g1r1:
        return fbFetch_a1b1g1r1;
    case PICT_c4:
        return fbFetch_c4;
    case PICT_g4:
        return fbFetch_c4;

        /* 1bpp formats */
    case PICT_a1:
        return fbFetch_a1;
    case PICT_g1:
        return fbFetch_g1;
    }

    return NULL;
}

/*
 * Pixel wise fetching
 */

typedef FASTCALL CARD32 (*fetchPixelProc) (const FbBits * bits, int offset,
                                           miIndexedPtr indexed);

static FASTCALL CARD32
fbFetchPixel_a8r8g8b8(const FbBits * bits, int offset, miIndexedPtr indexed)
{
    return ((CARD32 *) bits)[offset];
}

static FASTCALL CARD32
fbFetchPixel_x8r8g8b8(const FbBits * bits, int offset, miIndexedPtr indexed)
{
    return ((CARD32 *) bits)[offset] | 0xff000000;
}

static FASTCALL CARD32
fbFetchPixel_a8b8g8r8(const FbBits * bits, int offset, miIndexedPtr indexed)
{
    CARD32 pixel = ((CARD32 *) bits)[offset];

    return ((pixel & 0xff000000) |
            ((pixel >> 16) & 0xff) |
            (pixel & 0x0000ff00) | ((pixel & 0xff) << 16));
}

static FASTCALL CARD32
fbFetchPixel_x8b8g8r8(const FbBits * bits, int offset, miIndexedPtr indexed)
{
    CARD32 pixel = ((CARD32 *) bits)[offset];

    return ((0xff000000) |
            ((pixel >> 16) & 0xff) |
            (pixel & 0x0000ff00) | ((pixel & 0xff) << 16));
}

static FASTCALL CARD32
fbFetchPixel_r8g8b8(const FbBits * bits, int offset, miIndexedPtr indexed)
{
    CARD8 *pixel = ((CARD8 *) bits) + (offset * 3);

#if IMAGE_BYTE_ORDER == MSBFirst
    return (0xff000000 | (pixel[0] << 16) | (pixel[1] << 8) | (pixel[2]));
#else
    return (0xff000000 | (pixel[2] << 16) | (pixel[1] << 8) | (pixel[0]));
#endif
}

static FASTCALL CARD32
fbFetchPixel_b8g8r8(const FbBits * bits, int offset, miIndexedPtr indexed)
{
    CARD8 *pixel = ((CARD8 *) bits) + (offset * 3);

#if IMAGE_BYTE_ORDER == MSBFirst
    return (0xff000000 | (pixel[2] << 16) | (pixel[1] << 8) | (pixel[0]));
#else
    return (0xff000000 | (pixel[0] << 16) | (pixel[1] << 8) | (pixel[2]));
#endif
}

static FASTCALL CARD32
fbFetchPixel_r5g6b5(const FbBits * bits, int offset, miIndexedPtr indexed)
{
    CARD32 pixel = ((CARD16 *) bits)[offset];

    CARD32 r, g, b;

    r = ((pixel & 0xf800) | ((pixel & 0xe000) >> 5)) << 8;
    g = ((pixel & 0x07e0) | ((pixel & 0x0600) >> 6)) << 5;
    b = ((pixel & 0x001c) | ((pixel & 0x001f) << 5)) >> 2;
    return (0xff000000 | r | g | b);
}

static FASTCALL CARD32
fbFetchPixel_b5g6r5(const FbBits * bits, int offset, miIndexedPtr indexed)
{
    CARD32 pixel = ((CARD16 *) bits)[offset];

    CARD32 r, g, b;

    b = ((pixel & 0xf800) | ((pixel & 0xe000) >> 5)) >> 8;
    g = ((pixel & 0x07e0) | ((pixel & 0x0600) >> 6)) << 5;
    r = ((pixel & 0x001c) | ((pixel & 0x001f) << 5)) << 14;
    return (0xff000000 | r | g | b);
}

static FASTCALL CARD32
fbFetchPixel_a1r5g5b5(const FbBits * bits, int offset, miIndexedPtr indexed)
{
    CARD32 pixel = ((CARD16 *) bits)[offset];

    CARD32 a, r, g, b;

    a = (CARD32) ((CARD8) (0 - ((pixel & 0x8000) >> 15))) << 24;
    r = ((pixel & 0x7c00) | ((pixel & 0x7000) >> 5)) << 9;
    g = ((pixel & 0x03e0) | ((pixel & 0x0380) >> 5)) << 6;
    b = ((pixel & 0x001c) | ((pixel & 0x001f) << 5)) >> 2;
    return (a | r | g | b);
}

static FASTCALL CARD32
fbFetchPixel_x1r5g5b5(const FbBits * bits, int offset, miIndexedPtr indexed)
{
    CARD32 pixel = ((CARD16 *) bits)[offset];

    CARD32 r, g, b;

    r = ((pixel & 0x7c00) | ((pixel & 0x7000) >> 5)) << 9;
    g = ((pixel & 0x03e0) | ((pixel & 0x0380) >> 5)) << 6;
    b = ((pixel & 0x001c) | ((pixel & 0x001f) << 5)) >> 2;
    return (0xff000000 | r | g | b);
}

static FASTCALL CARD32
fbFetchPixel_a1b5g5r5(const FbBits * bits, int offset, miIndexedPtr indexed)
{
    CARD32 pixel = ((CARD16 *) bits)[offset];

    CARD32 a, r, g, b;

    a = (CARD32) ((CARD8) (0 - ((pixel & 0x8000) >> 15))) << 24;
    b = ((pixel & 0x7c00) | ((pixel & 0x7000) >> 5)) >> 7;
    g = ((pixel & 0x03e0) | ((pixel & 0x0380) >> 5)) << 6;
    r = ((pixel & 0x001c) | ((pixel & 0x001f) << 5)) << 14;
    return (a | r | g | b);
}

static FASTCALL CARD32
fbFetchPixel_x1b5g5r5(const FbBits * bits, int offset, miIndexedPtr indexed)
{
    CARD32 pixel = ((CARD16 *) bits)[offset];

    CARD32 r, g, b;

    b = ((pixel & 0x7c00) | ((pixel & 0x7000) >> 5)) >> 7;
    g = ((pixel & 0x03e0) | ((pixel & 0x0380) >> 5)) << 6;
    r = ((pixel & 0x001c) | ((pixel & 0x001f) << 5)) << 14;
    return (0xff000000 | r | g | b);
}

static FASTCALL CARD32
fbFetchPixel_a4r4g4b4(const FbBits * bits, int offset, miIndexedPtr indexed)
{
    CARD32 pixel = ((CARD16 *) bits)[offset];

    CARD32 a, r, g, b;

    a = ((pixel & 0xf000) | ((pixel & 0xf000) >> 4)) << 16;
    r = ((pixel & 0x0f00) | ((pixel & 0x0f00) >> 4)) << 12;
    g = ((pixel & 0x00f0) | ((pixel & 0x00f0) >> 4)) << 8;
    b = ((pixel & 0x000f) | ((pixel & 0x000f) << 4));
    return (a | r | g | b);
}

static FASTCALL CARD32
fbFetchPixel_x4r4g4b4(const FbBits * bits, int offset, miIndexedPtr indexed)
{
    CARD32 pixel = ((CARD16 *) bits)[offset];

    CARD32 r, g, b;

    r = ((pixel & 0x0f00) | ((pixel & 0x0f00) >> 4)) << 12;
    g = ((pixel & 0x00f0) | ((pixel & 0x00f0) >> 4)) << 8;
    b = ((pixel & 0x000f) | ((pixel & 0x000f) << 4));
    return (0xff000000 | r | g | b);
}

static FASTCALL CARD32
fbFetchPixel_a4b4g4r4(const FbBits * bits, int offset, miIndexedPtr indexed)
{
    CARD32 pixel = ((CARD16 *) bits)[offset];

    CARD32 a, r, g, b;

    a = ((pixel & 0xf000) | ((pixel & 0xf000) >> 4)) << 16;
    b = ((pixel & 0x0f00) | ((pixel & 0x0f00) >> 4)) >> 4;
    g = ((pixel & 0x00f0) | ((pixel & 0x00f0) >> 4)) << 8;
    r = ((pixel & 0x000f) | ((pixel & 0x000f) << 4)) << 16;
    return (a | r | g | b);
}

static FASTCALL CARD32
fbFetchPixel_x4b4g4r4(const FbBits * bits, int offset, miIndexedPtr indexed)
{
    CARD32 pixel = ((CARD16 *) bits)[offset];

    CARD32 r, g, b;

    b = ((pixel & 0x0f00) | ((pixel & 0x0f00) >> 4)) >> 4;
    g = ((pixel & 0x00f0) | ((pixel & 0x00f0) >> 4)) << 8;
    r = ((pixel & 0x000f) | ((pixel & 0x000f) << 4)) << 16;
    return (0xff000000 | r | g | b);
}

static FASTCALL CARD32
fbFetchPixel_a8(const FbBits * bits, int offset, miIndexedPtr indexed)
{
    CARD32 pixel = ((CARD8 *) bits)[offset];

    return pixel << 24;
}

static FASTCALL CARD32
fbFetchPixel_r3g3b2(const FbBits * bits, int offset, miIndexedPtr indexed)
{
    CARD32 pixel = ((CARD8 *) bits)[offset];

    CARD32 r, g, b;

    r = ((pixel & 0xe0) | ((pixel & 0xe0) >> 3) | ((pixel & 0xc0) >> 6)) << 16;
    g = ((pixel & 0x1c) | ((pixel & 0x18) >> 3) | ((pixel & 0x1c) << 3)) << 8;
    b = (((pixel & 0x03)) |
         ((pixel & 0x03) << 2) | ((pixel & 0x03) << 4) | ((pixel & 0x03) << 6));
    return (0xff000000 | r | g | b);
}

static FASTCALL CARD32
fbFetchPixel_b2g3r3(const FbBits * bits, int offset, miIndexedPtr indexed)
{
    CARD32 pixel = ((CARD8 *) bits)[offset];

    CARD32 r, g, b;

    b = (((pixel & 0xc0)) |
         ((pixel & 0xc0) >> 2) | ((pixel & 0xc0) >> 4) | ((pixel & 0xc0) >> 6));
    g = ((pixel & 0x38) | ((pixel & 0x38) >> 3) | ((pixel & 0x30) << 2)) << 8;
    r = (((pixel & 0x07)) |
         ((pixel & 0x07) << 3) | ((pixel & 0x06) << 6)) << 16;
    return (0xff000000 | r | g | b);
}

static FASTCALL CARD32
fbFetchPixel_a2r2g2b2(const FbBits * bits, int offset, miIndexedPtr indexed)
{
    CARD32 pixel = ((CARD8 *) bits)[offset];

    CARD32 a, r, g, b;

    a = ((pixel & 0xc0) * 0x55) << 18;
    r = ((pixel & 0x30) * 0x55) << 12;
    g = ((pixel & 0x0c) * 0x55) << 6;
    b = ((pixel & 0x03) * 0x55);
    return a | r | g | b;
}

static FASTCALL CARD32
fbFetchPixel_a2b2g2r2(const FbBits * bits, int offset, miIndexedPtr indexed)
{
    CARD32 pixel = ((CARD8 *) bits)[offset];

    CARD32 a, r, g, b;

    a = ((pixel & 0xc0) * 0x55) << 18;
    b = ((pixel & 0x30) * 0x55) >> 6;
    g = ((pixel & 0x0c) * 0x55) << 6;
    r = ((pixel & 0x03) * 0x55) << 16;
    return a | r | g | b;
}

static FASTCALL CARD32
fbFetchPixel_c8(const FbBits * bits, int offset, miIndexedPtr indexed)
{
    CARD32 pixel = ((CARD8 *) bits)[offset];

    return indexed->rgba[pixel];
}

static FASTCALL CARD32
fbFetchPixel_x4a4(const FbBits * bits, int offset, miIndexedPtr indexed)
{
    CARD32 pixel = ((CARD8 *) bits)[offset];

    return ((pixel & 0xf) | ((pixel & 0xf) << 4)) << 24;
}

#define Fetch8(l,o)    (((CARD8 *) (l))[(o) >> 2])
#if IMAGE_BYTE_ORDER == MSBFirst
#define Fetch4(l,o)    ((o) & 2 ? Fetch8(l,o) & 0xf : Fetch8(l,o) >> 4)
#else
#define Fetch4(l,o)    ((o) & 2 ? Fetch8(l,o) >> 4 : Fetch8(l,o) & 0xf)
#endif

static FASTCALL CARD32
fbFetchPixel_a4(const FbBits * bits, int offset, miIndexedPtr indexed)
{
    CARD32 pixel = Fetch4(bits, offset);

    pixel |= pixel << 4;
    return pixel << 24;
}

static FASTCALL CARD32
fbFetchPixel_r1g2b1(const FbBits * bits, int offset, miIndexedPtr indexed)
{
    CARD32 pixel = Fetch4(bits, offset);

    CARD32 r, g, b;

    r = ((pixel & 0x8) * 0xff) << 13;
    g = ((pixel & 0x6) * 0x55) << 7;
    b = ((pixel & 0x1) * 0xff);
    return 0xff000000 | r | g | b;
}

static FASTCALL CARD32
fbFetchPixel_b1g2r1(const FbBits * bits, int offset, miIndexedPtr indexed)
{
    CARD32 pixel = Fetch4(bits, offset);

    CARD32 r, g, b;

    b = ((pixel & 0x8) * 0xff) >> 3;
    g = ((pixel & 0x6) * 0x55) << 7;
    r = ((pixel & 0x1) * 0xff) << 16;
    return 0xff000000 | r | g | b;
}

static FASTCALL CARD32
fbFetchPixel_a1r1g1b1(const FbBits * bits, int offset, miIndexedPtr indexed)
{
    CARD32 pixel = Fetch4(bits, offset);

    CARD32 a, r, g, b;

    a = ((pixel & 0x8) * 0xff) << 21;
    r = ((pixel & 0x4) * 0xff) << 14;
    g = ((pixel & 0x2) * 0xff) << 7;
    b = ((pixel & 0x1) * 0xff);
    return a | r | g | b;
}

static FASTCALL CARD32
fbFetchPixel_a1b1g1r1(const FbBits * bits, int offset, miIndexedPtr indexed)
{
    CARD32 pixel = Fetch4(bits, offset);

    CARD32 a, r, g, b;

    a = ((pixel & 0x8) * 0xff) << 21;
    r = ((pixel & 0x4) * 0xff) >> 3;
    g = ((pixel & 0x2) * 0xff) << 7;
    b = ((pixel & 0x1) * 0xff) << 16;
    return a | r | g | b;
}

static FASTCALL CARD32
fbFetchPixel_c4(const FbBits * bits, int offset, miIndexedPtr indexed)
{
    CARD32 pixel = Fetch4(bits, offset);

    return indexed->rgba[pixel];
}

static FASTCALL CARD32
fbFetchPixel_a1(const FbBits * bits, int offset, miIndexedPtr indexed)
{
    CARD32 pixel = ((CARD32 *) bits)[offset >> 5];

    CARD32 a;

#if BITMAP_BIT_ORDER == MSBFirst
    a = pixel >> (0x1f - (offset & 0x1f));
#else
    a = pixel >> (offset & 0x1f);
#endif
    a = a & 1;
    a |= a << 1;
    a |= a << 2;
    a |= a << 4;
    return a << 24;
}

static FASTCALL CARD32
fbFetchPixel_g1(const FbBits * bits, int offset, miIndexedPtr indexed)
{
    CARD32 pixel = ((CARD32 *) bits)[offset >> 5];

    CARD32 a;

#if BITMAP_BIT_ORDER == MSBFirst
    a = pixel >> (0x1f - (offset & 0x1f));
#else
    a = pixel >> (offset & 0x1f);
#endif
    a = a & 1;
    return indexed->rgba[a];
}

static fetchPixelProc
fetchPixelProcForPicture(PicturePtr pict)
{
    switch (pict->format) {
    case PICT_a8r8g8b8:
        return fbFetchPixel_a8r8g8b8;
    case PICT_x8r8g8b8:
        return fbFetchPixel_x8r8g8b8;
    case PICT_a8b8g8r8:
        return fbFetchPixel_a8b8g8r8;
    case PICT_x8b8g8r8:
        return fbFetchPixel_x8b8g8r8;

        /* 24bpp formats */
    case PICT_r8g8b8:
        return fbFetchPixel_r8g8b8;
    case PICT_b8g8r8:
        return fbFetchPixel_b8g8r8;

        /* 16bpp formats */
    case PICT_r5g6b5:
        return fbFetchPixel_r5g6b5;
    case PICT_b5g6r5:
        return fbFetchPixel_b5g6r5;

    case PICT_a1r5g5b5:
        return fbFetchPixel_a1r5g5b5;
    case PICT_x1r5g5b5:
        return fbFetchPixel_x1r5g5b5;
    case PICT_a1b5g5r5:
        return fbFetchPixel_a1b5g5r5;
    case PICT_x1b5g5r5:
        return fbFetchPixel_x1b5g5r5;
    case PICT_a4r4g4b4:
        return fbFetchPixel_a4r4g4b4;
    case PICT_x4r4g4b4:
        return fbFetchPixel_x4r4g4b4;
    case PICT_a4b4g4r4:
        return fbFetchPixel_a4b4g4r4;
    case PICT_x4b4g4r4:
        return fbFetchPixel_x4b4g4r4;

        /* 8bpp formats */
    case PICT_a8:
        return fbFetchPixel_a8;
    case PICT_r3g3b2:
        return fbFetchPixel_r3g3b2;
    case PICT_b2g3r3:
        return fbFetchPixel_b2g3r3;
    case PICT_a2r2g2b2:
        return fbFetchPixel_a2r2g2b2;
    case PICT_a2b2g2r2:
        return fbFetchPixel_a2b2g2r2;
    case PICT_c8:
        return fbFetchPixel_c8;
    case PICT_g8:
        return fbFetchPixel_c8;
    case PICT_x4a4:
        return fbFetchPixel_x4a4;

        /* 4bpp formats */
    case PICT_a4:
        return fbFetchPixel_a4;
    case PICT_r1g2b1:
        return fbFetchPixel_r1g2b1;
    case PICT_b1g2r1:
        return fbFetchPixel_b1g2r1;
    case PICT_a1r1g1b1:
        return fbFetchPixel_a1r1g1b1;
    case PICT_a1b1g1r1:
        return fbFetchPixel_a1b1g1r1;
    case PICT_c4:
        return fbFetchPixel_c4;
    case PICT_g4:
        return fbFetchPixel_c4;

        /* 1bpp formats */
    case PICT_a1:
        return fbFetchPixel_a1;
    case PICT_g1:
        return fbFetchPixel_g1;
    }

    return NULL;
}

/*
 * All the store functions
 */

typedef FASTCALL void (*storeProc) (FbBits * bits, const CARD32 *values, int x,
                                    int width, miIndexedPtr indexed);

#define Splita(v)	CARD32	a = ((v) >> 24), r = ((v) >> 16) & 0xff, g = ((v) >> 8) & 0xff, b = (v) & 0xff
#define Split(v)	CARD32	r = ((v) >> 16) & 0xff, g = ((v) >> 8) & 0xff, b = (v) & 0xff

static FASTCALL void
fbStore_a8r8g8b8(FbBits * bits, const CARD32 *values, int x, int width,
                 miIndexedPtr indexed)
{
    memcpy(((CARD32 *) bits) + x, values, width * sizeof(CARD32));
}

static FASTCALL void
fbStore_x8r8g8b8(FbBits * bits, const CARD32 *values, int x, int width,
                 miIndexedPtr indexed)
{
    int i;

    CARD32 *pixel = (CARD32 *) bits + x;

    for (i = 0; i < width; ++i)
        *pixel++ = values[i] & 0xffffff;
}

static FASTCALL void
fbStore_a8b8g8r8(FbBits * bits, const CARD32 *values, int x, int width,
                 miIndexedPtr indexed)
{
    int i;

    CARD32 *pixel = (CARD32 *) bits + x;

    for (i = 0; i < width; ++i)
        *pixel++ =
            (values[i] & 0xff00ff00) | ((values[i] >> 16) & 0xff) |
            ((values[i] & 0xff) << 16);
}

static FASTCALL void
fbStore_x8b8g8r8(FbBits * bits, const CARD32 *values, int x, int width,
                 miIndexedPtr indexed)
{
    int i;

    CARD32 *pixel = (CARD32 *) bits + x;

    for (i = 0; i < width; ++i)
        *pixel++ =
            (values[i] & 0x0000ff00) | ((values[i] >> 16) & 0xff) |
            ((values[i] & 0xff) << 16);
}

static FASTCALL void
fbStore_r8g8b8(FbBits * bits, const CARD32 *values, int x, int width,
               miIndexedPtr indexed)
{
    int i;

    CARD8 *pixel = ((CARD8 *) bits) + 3 * x;

    for (i = 0; i < width; ++i) {
        Store24(pixel, values[i]);
        pixel += 3;
    }
}

static FASTCALL void
fbStore_b8g8r8(FbBits * bits, const CARD32 *values, int x, int width,
               miIndexedPtr indexed)
{
    int i;

    CARD8 *pixel = ((CARD8 *) bits) + 3 * x;

    for (i = 0; i < width; ++i) {
#if IMAGE_BYTE_ORDER == MSBFirst
        *pixel++ = Blue(values[i]);
        *pixel++ = Green(values[i]);
        *pixel++ = Red(values[i]);
#else
        *pixel++ = Red(values[i]);
        *pixel++ = Green(values[i]);
        *pixel++ = Blue(values[i]);
#endif
    }
}

static FASTCALL void
fbStore_r5g6b5(FbBits * bits, const CARD32 *values, int x, int width,
               miIndexedPtr indexed)
{
    int i;

    CARD16 *pixel = ((CARD16 *) bits) + x;

    for (i = 0; i < width; ++i) {
        CARD32 s = values[i];

        *pixel++ = ((s >> 3) & 0x001f) |
            ((s >> 5) & 0x07e0) | ((s >> 8) & 0xf800);
    }
}

static FASTCALL void
fbStore_b5g6r5(FbBits * bits, const CARD32 *values, int x, int width,
               miIndexedPtr indexed)
{
    int i;

    CARD16 *pixel = ((CARD16 *) bits) + x;

    for (i = 0; i < width; ++i) {
        Split(values[i]);
        *pixel++ = (((b << 8) & 0xf800) | ((g << 3) & 0x07e0) | ((r >> 3)));
    }
}

static FASTCALL void
fbStore_a1r5g5b5(FbBits * bits, const CARD32 *values, int x, int width,
                 miIndexedPtr indexed)
{
    int i;

    CARD16 *pixel = ((CARD16 *) bits) + x;

    for (i = 0; i < width; ++i) {
        Splita(values[i]);
        *pixel++ = (((a << 8) & 0x8000) |
                    ((r << 7) & 0x7c00) | ((g << 2) & 0x03e0) | ((b >> 3)));
    }
}

static FASTCALL void
fbStore_x1r5g5b5(FbBits * bits, const CARD32 *values, int x, int width,
                 miIndexedPtr indexed)
{
    int i;

    CARD16 *pixel = ((CARD16 *) bits) + x;

    for (i = 0; i < width; ++i) {
        Split(values[i]);
        *pixel++ = (((r << 7) & 0x7c00) | ((g << 2) & 0x03e0) | ((b >> 3)));
    }
}

static FASTCALL void
fbStore_a1b5g5r5(FbBits * bits, const CARD32 *values, int x, int width,
                 miIndexedPtr indexed)
{
    int i;

    CARD16 *pixel = ((CARD16 *) bits) + x;

    for (i = 0; i < width; ++i) {
        Splita(values[i]);
        *pixel++ = (((a << 8) & 0x8000) |
                    ((b << 7) & 0x7c00) | ((g << 2) & 0x03e0) | ((r >> 3)));
    }
}

static FASTCALL void
fbStore_x1b5g5r5(FbBits * bits, const CARD32 *values, int x, int width,
                 miIndexedPtr indexed)
{
    int i;

    CARD16 *pixel = ((CARD16 *) bits) + x;

    for (i = 0; i < width; ++i) {
        Split(values[i]);
        *pixel++ = (((b << 7) & 0x7c00) | ((g << 2) & 0x03e0) | ((r >> 3)));
    }
}

static FASTCALL void
fbStore_a4r4g4b4(FbBits * bits, const CARD32 *values, int x, int width,
                 miIndexedPtr indexed)
{
    int i;

    CARD16 *pixel = ((CARD16 *) bits) + x;

    for (i = 0; i < width; ++i) {
        Splita(values[i]);
        *pixel++ = (((a << 8) & 0xf000) |
                    ((r << 4) & 0x0f00) | ((g) & 0x00f0) | ((b >> 4)));
    }
}

static FASTCALL void
fbStore_x4r4g4b4(FbBits * bits, const CARD32 *values, int x, int width,
                 miIndexedPtr indexed)
{
    int i;

    CARD16 *pixel = ((CARD16 *) bits) + x;

    for (i = 0; i < width; ++i) {
        Split(values[i]);
        *pixel++ = (((r << 4) & 0x0f00) | ((g) & 0x00f0) | ((b >> 4)));
    }
}

static FASTCALL void
fbStore_a4b4g4r4(FbBits * bits, const CARD32 *values, int x, int width,
                 miIndexedPtr indexed)
{
    int i;

    CARD16 *pixel = ((CARD16 *) bits) + x;

    for (i = 0; i < width; ++i) {
        Splita(values[i]);
        *pixel++ = (((a << 8) & 0xf000) |
                    ((b << 4) & 0x0f00) | ((g) & 0x00f0) | ((r >> 4)));
    }
}

static FASTCALL void
fbStore_x4b4g4r4(FbBits * bits, const CARD32 *values, int x, int width,
                 miIndexedPtr indexed)
{
    int i;

    CARD16 *pixel = ((CARD16 *) bits) + x;

    for (i = 0; i < width; ++i) {
        Split(values[i]);
        *pixel++ = (((b << 4) & 0x0f00) | ((g) & 0x00f0) | ((r >> 4)));
    }
}

static FASTCALL void
fbStore_a8(FbBits * bits, const CARD32 *values, int x, int width,
           miIndexedPtr indexed)
{
    int i;

    CARD8 *pixel = ((CARD8 *) bits) + x;

    for (i = 0; i < width; ++i) {
        *pixel++ = values[i] >> 24;
    }
}

static FASTCALL void
fbStore_r3g3b2(FbBits * bits, const CARD32 *values, int x, int width,
               miIndexedPtr indexed)
{
    int i;

    CARD8 *pixel = ((CARD8 *) bits) + x;

    for (i = 0; i < width; ++i) {
        Split(values[i]);
        *pixel++ = (((r) & 0xe0) | ((g >> 3) & 0x1c) | ((b >> 6)));
    }
}

static FASTCALL void
fbStore_b2g3r3(FbBits * bits, const CARD32 *values, int x, int width,
               miIndexedPtr indexed)
{
    int i;

    CARD8 *pixel = ((CARD8 *) bits) + x;

    for (i = 0; i < width; ++i) {
        Split(values[i]);
        *pixel++ = (((b) & 0xe0) | ((g >> 3) & 0x1c) | ((r >> 6)));
    }
}

static FASTCALL void
fbStore_a2r2g2b2(FbBits * bits, const CARD32 *values, int x, int width,
                 miIndexedPtr indexed)
{
    int i;

    CARD8 *pixel = ((CARD8 *) bits) + x;

    for (i = 0; i < width; ++i) {
        Splita(values[i]);
        *pixel++ = (((a) & 0xc0) |
                    ((r >> 2) & 0x30) | ((g >> 4) & 0x0c) | ((b >> 6)));
    }
}

static FASTCALL void
fbStore_c8(FbBits * bits, const CARD32 *values, int x, int width,
           miIndexedPtr indexed)
{
    int i;

    CARD8 *pixel = ((CARD8 *) bits) + x;

    for (i = 0; i < width; ++i) {
        *pixel++ = miIndexToEnt24(indexed, values[i]);
    }
}

static FASTCALL void
fbStore_x4a4(FbBits * bits, const CARD32 *values, int x, int width,
             miIndexedPtr indexed)
{
    int i;

    CARD8 *pixel = ((CARD8 *) bits) + x;

    for (i = 0; i < width; ++i) {
        *pixel++ = values[i] >> 28;
    }
}

#define Store8(l,o,v)  (((CARD8 *) l)[(o) >> 3] = (v))
#if IMAGE_BYTE_ORDER == MSBFirst
#define Store4(l,o,v)  Store8(l,o,((o) & 4 ? \
				   (Fetch8(l,o) & 0xf0) | (v) : \
				   (Fetch8(l,o) & 0x0f) | ((v) << 4)))
#else
#define Store4(l,o,v)  Store8(l,o,((o) & 4 ? \
				   (Fetch8(l,o) & 0x0f) | ((v) << 4) : \
				   (Fetch8(l,o) & 0xf0) | (v)))
#endif

static FASTCALL void
fbStore_a4(FbBits * bits, const CARD32 *values, int x, int width,
           miIndexedPtr indexed)
{
    int i;

    for (i = 0; i < width; ++i) {
        Store4(bits, i + x, values[i] >> 28);
    }
}

static FASTCALL void
fbStore_r1g2b1(FbBits * bits, const CARD32 *values, int x, int width,
               miIndexedPtr indexed)
{
    int i;

    for (i = 0; i < width; ++i) {
        CARD32 pixel;

        Split(values[i]);
        pixel = (((r >> 4) & 0x8) | ((g >> 5) & 0x6) | ((b >> 7)));
        Store4(bits, i + x, pixel);
    }
}

static FASTCALL void
fbStore_b1g2r1(FbBits * bits, const CARD32 *values, int x, int width,
               miIndexedPtr indexed)
{
    int i;

    for (i = 0; i < width; ++i) {
        CARD32 pixel;

        Split(values[i]);
        pixel = (((b >> 4) & 0x8) | ((g >> 5) & 0x6) | ((r >> 7)));
        Store4(bits, i + x, pixel);
    }
}

static FASTCALL void
fbStore_a1r1g1b1(FbBits * bits, const CARD32 *values, int x, int width,
                 miIndexedPtr indexed)
{
    int i;

    for (i = 0; i < width; ++i) {
        CARD32 pixel;

        Splita(values[i]);
        pixel = (((a >> 4) & 0x8) |
                 ((r >> 5) & 0x4) | ((g >> 6) & 0x2) | ((b >> 7)));
        Store4(bits, i + x, pixel);
    }
}

static FASTCALL void
fbStore_a1b1g1r1(FbBits * bits, const CARD32 *values, int x, int width,
                 miIndexedPtr indexed)
{
    int i;

    for (i = 0; i < width; ++i) {
        CARD32 pixel;

        Splita(values[i]);
        pixel = (((a >> 4) & 0x8) |
                 ((b >> 5) & 0x4) | ((g >> 6) & 0x2) | ((r >> 7)));
        Store4(bits, i + x, pixel);
    }
}

static FASTCALL void
fbStore_c4(FbBits * bits, const CARD32 *values, int x, int width,
           miIndexedPtr indexed)
{
    int i;

    for (i = 0; i < width; ++i) {
        CARD32 pixel;

        pixel = miIndexToEnt24(indexed, values[i]);
        Store4(bits, i + x, pixel);
    }
}

static FASTCALL void
fbStore_a1(FbBits * bits, const CARD32 *values, int x, int width,
           miIndexedPtr indexed)
{
    int i;

    for (i = 0; i < width; ++i) {
        CARD32 *pixel = ((CARD32 *) bits) + ((i + x) >> 5);

        CARD32 mask = FbStipMask((i + x) & 0x1f, 1);

        CARD32 v = values[i] & 0x80000000 ? mask : 0;

        *pixel = (*pixel & ~mask) | v;
    }
}

static FASTCALL void
fbStore_g1(FbBits * bits, const CARD32 *values, int x, int width,
           miIndexedPtr indexed)
{
    int i;

    for (i = 0; i < width; ++i) {
        CARD32 *pixel = ((CARD32 *) bits) + ((i + x) >> 5);

        CARD32 mask = FbStipMask((i + x) & 0x1f, 1);

        CARD32 v = miIndexToEntY24(indexed, values[i]) ? mask : 0;

        *pixel = (*pixel & ~mask) | v;
    }
}

static storeProc
storeProcForPicture(PicturePtr pict)
{
    switch (pict->format) {
    case PICT_a8r8g8b8:
        return fbStore_a8r8g8b8;
    case PICT_x8r8g8b8:
        return fbStore_x8r8g8b8;
    case PICT_a8b8g8r8:
        return fbStore_a8b8g8r8;
    case PICT_x8b8g8r8:
        return fbStore_x8b8g8r8;

        /* 24bpp formats */
    case PICT_r8g8b8:
        return fbStore_r8g8b8;
    case PICT_b8g8r8:
        return fbStore_b8g8r8;

        /* 16bpp formats */
    case PICT_r5g6b5:
        return fbStore_r5g6b5;
    case PICT_b5g6r5:
        return fbStore_b5g6r5;

    case PICT_a1r5g5b5:
        return fbStore_a1r5g5b5;
    case PICT_x1r5g5b5:
        return fbStore_x1r5g5b5;
    case PICT_a1b5g5r5:
        return fbStore_a1b5g5r5;
    case PICT_x1b5g5r5:
        return fbStore_x1b5g5r5;
    case PICT_a4r4g4b4:
        return fbStore_a4r4g4b4;
    case PICT_x4r4g4b4:
        return fbStore_x4r4g4b4;
    case PICT_a4b4g4r4:
        return fbStore_a4b4g4r4;
    case PICT_x4b4g4r4:
        return fbStore_x4b4g4r4;

        /* 8bpp formats */
    case PICT_a8:
        return fbStore_a8;
    case PICT_r3g3b2:
        return fbStore_r3g3b2;
    case PICT_b2g3r3:
        return fbStore_b2g3r3;
    case PICT_a2r2g2b2:
        return fbStore_a2r2g2b2;
    case PICT_c8:
        return fbStore_c8;
    case PICT_g8:
        return fbStore_c8;
    case PICT_x4a4:
        return fbStore_x4a4;

        /* 4bpp formats */
    case PICT_a4:
        return fbStore_a4;
    case PICT_r1g2b1:
        return fbStore_r1g2b1;
    case PICT_b1g2r1:
        return fbStore_b1g2r1;
    case PICT_a1r1g1b1:
        return fbStore_a1r1g1b1;
    case PICT_a1b1g1r1:
        return fbStore_a1b1g1r1;
    case PICT_c4:
        return fbStore_c4;
    case PICT_g4:
        return fbStore_c4;

        /* 1bpp formats */
    case PICT_a1:
        return fbStore_a1;
    case PICT_g1:
        return fbStore_g1;
    default:
        return NULL;
    }
}

/*
 * Combine src and mask
 */
static FASTCALL void
fbCombineMaskU(CARD32 *src, const CARD32 *mask, int width)
{
    int i;

    for (i = 0; i < width; ++i) {
        CARD32 a = mask[i] >> 24;

        CARD32 s = src[i];

        FbByteMul(s, a);
        src[i] = s;
    }
}

/*
 * All of the composing functions
 */

static FASTCALL void
fbCombineClear(CARD32 *dest, const CARD32 *src, int width)
{
    memset(dest, 0, width * sizeof(CARD32));
}

static FASTCALL void
fbCombineSrcU(CARD32 *dest, const CARD32 *src, int width)
{
    memcpy(dest, src, width * sizeof(CARD32));
}

static FASTCALL void
fbCombineOverU(CARD32 *dest, const CARD32 *src, int width)
{
    int i;

    for (i = 0; i < width; ++i) {
        CARD32 s = src[i];

        CARD32 d = dest[i];

        CARD32 ia = Alpha(~s);

        FbByteMulAdd(d, ia, s);
        dest[i] = d;
    }
}

static FASTCALL void
fbCombineOverReverseU(CARD32 *dest, const CARD32 *src, int width)
{
    int i;

    for (i = 0; i < width; ++i) {
        CARD32 s = src[i];

        CARD32 d = dest[i];

        CARD32 ia = Alpha(~dest[i]);

        FbByteMulAdd(s, ia, d);
        dest[i] = s;
    }
}

static FASTCALL void
fbCombineInU(CARD32 *dest, const CARD32 *src, int width)
{
    int i;

    for (i = 0; i < width; ++i) {
        CARD32 s = src[i];

        CARD32 a = Alpha(dest[i]);

        FbByteMul(s, a);
        dest[i] = s;
    }
}

static FASTCALL void
fbCombineInReverseU(CARD32 *dest, const CARD32 *src, int width)
{
    int i;

    for (i = 0; i < width; ++i) {
        CARD32 d = dest[i];

        CARD32 a = Alpha(src[i]);

        FbByteMul(d, a);
        dest[i] = d;
    }
}

static FASTCALL void
fbCombineOutU(CARD32 *dest, const CARD32 *src, int width)
{
    int i;

    for (i = 0; i < width; ++i) {
        CARD32 s = src[i];

        CARD32 a = Alpha(~dest[i]);

        FbByteMul(s, a);
        dest[i] = s;
    }
}

static FASTCALL void
fbCombineOutReverseU(CARD32 *dest, const CARD32 *src, int width)
{
    int i;

    for (i = 0; i < width; ++i) {
        CARD32 d = dest[i];

        CARD32 a = Alpha(~src[i]);

        FbByteMul(d, a);
        dest[i] = d;
    }
}

static FASTCALL void
fbCombineAtopU(CARD32 *dest, const CARD32 *src, int width)
{
    int i;

    for (i = 0; i < width; ++i) {
        CARD32 s = src[i];

        CARD32 d = dest[i];

        CARD32 dest_a = Alpha(d);

        CARD32 src_ia = Alpha(~s);

        FbByteAddMul(s, dest_a, d, src_ia);
        dest[i] = s;
    }
}

static FASTCALL void
fbCombineAtopReverseU(CARD32 *dest, const CARD32 *src, int width)
{
    int i;

    for (i = 0; i < width; ++i) {
        CARD32 s = src[i];

        CARD32 d = dest[i];

        CARD32 src_a = Alpha(s);

        CARD32 dest_ia = Alpha(~d);

        FbByteAddMul(s, dest_ia, d, src_a);
        dest[i] = s;
    }
}

static FASTCALL void
fbCombineXorU(CARD32 *dest, const CARD32 *src, int width)
{
    int i;

    for (i = 0; i < width; ++i) {
        CARD32 s = src[i];

        CARD32 d = dest[i];

        CARD32 src_ia = Alpha(~s);

        CARD32 dest_ia = Alpha(~d);

        FbByteAddMul(s, dest_ia, d, src_ia);
        dest[i] = s;
    }
}

static FASTCALL void
fbCombineAddU(CARD32 *dest, const CARD32 *src, int width)
{
    int i;

    for (i = 0; i < width; ++i) {
        CARD32 s = src[i];

        CARD32 d = dest[i];

        FbByteAdd(d, s);
        dest[i] = d;
    }
}

static FASTCALL void
fbCombineSaturateU(CARD32 *dest, const CARD32 *src, int width)
{
    int i;

    for (i = 0; i < width; ++i) {
        CARD32 s = src[i];

        CARD32 d = dest[i];

        CARD16 sa, da;

        sa = s >> 24;
        da = ~d >> 24;
        if (sa > da) {
            sa = FbIntDiv(da, sa);
            FbByteMul(s, sa);
        }
        FbByteAdd(d, s);
        dest[i] = d;
    }
}

/*
 * All of the disjoint composing functions

 The four entries in the first column indicate what source contributions
 come from each of the four areas of the picture -- areas covered by neither
 A nor B, areas covered only by A, areas covered only by B and finally
 areas covered by both A and B.

		Disjoint			Conjoint
		Fa		Fb		Fa		Fb
(0,0,0,0)	0		0		0		0
(0,A,0,A)	1		0		1		0
(0,0,B,B)	0		1		0		1
(0,A,B,A)	1		min((1-a)/b,1)	1		max(1-a/b,0)
(0,A,B,B)	min((1-b)/a,1)	1		max(1-b/a,0)	1
(0,0,0,A)	max(1-(1-b)/a,0) 0		min(1,b/a)	0
(0,0,0,B)	0		max(1-(1-a)/b,0) 0		min(a/b,1)
(0,A,0,0)	min(1,(1-b)/a)	0		max(1-b/a,0)	0
(0,0,B,0)	0		min(1,(1-a)/b)	0		max(1-a/b,0)
(0,0,B,A)	max(1-(1-b)/a,0) min(1,(1-a)/b)	 min(1,b/a)	max(1-a/b,0)
(0,A,0,B)	min(1,(1-b)/a)	max(1-(1-a)/b,0) max(1-b/a,0)	min(1,a/b)
(0,A,B,0)	min(1,(1-b)/a)	min(1,(1-a)/b)	max(1-b/a,0)	max(1-a/b,0)

 */

#define CombineAOut 1
#define CombineAIn  2
#define CombineBOut 4
#define CombineBIn  8

#define CombineClear	0
#define CombineA	(CombineAOut|CombineAIn)
#define CombineB	(CombineBOut|CombineBIn)
#define CombineAOver	(CombineAOut|CombineBOut|CombineAIn)
#define CombineBOver	(CombineAOut|CombineBOut|CombineBIn)
#define CombineAAtop	(CombineBOut|CombineAIn)
#define CombineBAtop	(CombineAOut|CombineBIn)
#define CombineXor	(CombineAOut|CombineBOut)

/* portion covered by a but not b */
static INLINE CARD8
fbCombineDisjointOutPart(CARD8 a, CARD8 b)
{
    /* min (1, (1-b) / a) */

    b = ~b;                     /* 1 - b */
    if (b >= a)                 /* 1 - b >= a -> (1-b)/a >= 1 */
        return 0xff;            /* 1 */
    return FbIntDiv(b, a);      /* (1-b) / a */
}

/* portion covered by both a and b */
static INLINE CARD8
fbCombineDisjointInPart(CARD8 a, CARD8 b)
{
    /* max (1-(1-b)/a,0) */
    /*  = - min ((1-b)/a - 1, 0) */
    /*  = 1 - min (1, (1-b)/a) */

    b = ~b;                     /* 1 - b */
    if (b >= a)                 /* 1 - b >= a -> (1-b)/a >= 1 */
        return 0;               /* 1 - 1 */
    return ~FbIntDiv(b, a);     /* 1 - (1-b) / a */
}

static FASTCALL void
fbCombineDisjointGeneralU(CARD32 *dest, const CARD32 *src, int width,
                          CARD8 combine)
{
    int i;

    for (i = 0; i < width; ++i) {
        CARD32 s = src[i];

        CARD32 d = dest[i];

        CARD32 m, n, o, p;

        CARD16 Fa, Fb, t, u, v;

        CARD8 sa = s >> 24;

        CARD8 da = d >> 24;

        switch (combine & CombineA) {
        default:
            Fa = 0;
            break;
        case CombineAOut:
            Fa = fbCombineDisjointOutPart(sa, da);
            break;
        case CombineAIn:
            Fa = fbCombineDisjointInPart(sa, da);
            break;
        case CombineA:
            Fa = 0xff;
            break;
        }

        switch (combine & CombineB) {
        default:
            Fb = 0;
            break;
        case CombineBOut:
            Fb = fbCombineDisjointOutPart(da, sa);
            break;
        case CombineBIn:
            Fb = fbCombineDisjointInPart(da, sa);
            break;
        case CombineB:
            Fb = 0xff;
            break;
        }
        m = FbGen(s, d, 0, Fa, Fb, t, u, v);
        n = FbGen(s, d, 8, Fa, Fb, t, u, v);
        o = FbGen(s, d, 16, Fa, Fb, t, u, v);
        p = FbGen(s, d, 24, Fa, Fb, t, u, v);
        s = m | n | o | p;
        dest[i] = s;
    }
}

static FASTCALL void
fbCombineDisjointOverU(CARD32 *dest, const CARD32 *src, int width)
{
    int i;

    for (i = 0; i < width; ++i) {
        CARD32 s = src[i];

        CARD16 a = s >> 24;

        if (a != 0x00) {
            if (a != 0xff) {
                CARD32 d = dest[i];

                a = fbCombineDisjointOutPart(d >> 24, a);
                FbByteMulAdd(d, a, s);
                s = d;
            }
            dest[i] = s;
        }
    }
}

static FASTCALL void
fbCombineDisjointInU(CARD32 *dest, const CARD32 *src, int width)
{
    fbCombineDisjointGeneralU(dest, src, width, CombineAIn);
}

static FASTCALL void
fbCombineDisjointInReverseU(CARD32 *dest, const CARD32 *src, int width)
{
    fbCombineDisjointGeneralU(dest, src, width, CombineBIn);
}

static FASTCALL void
fbCombineDisjointOutU(CARD32 *dest, const CARD32 *src, int width)
{
    fbCombineDisjointGeneralU(dest, src, width, CombineAOut);
}

static FASTCALL void
fbCombineDisjointOutReverseU(CARD32 *dest, const CARD32 *src, int width)
{
    fbCombineDisjointGeneralU(dest, src, width, CombineBOut);
}

static FASTCALL void
fbCombineDisjointAtopU(CARD32 *dest, const CARD32 *src, int width)
{
    fbCombineDisjointGeneralU(dest, src, width, CombineAAtop);
}

static FASTCALL void
fbCombineDisjointAtopReverseU(CARD32 *dest, const CARD32 *src, int width)
{
    fbCombineDisjointGeneralU(dest, src, width, CombineBAtop);
}

static FASTCALL void
fbCombineDisjointXorU(CARD32 *dest, const CARD32 *src, int width)
{
    fbCombineDisjointGeneralU(dest, src, width, CombineXor);
}

/* portion covered by a but not b */
static INLINE CARD8
fbCombineConjointOutPart(CARD8 a, CARD8 b)
{
    /* max (1-b/a,0) */
    /* = 1-min(b/a,1) */

    /* min (1, (1-b) / a) */

    if (b >= a)                 /* b >= a -> b/a >= 1 */
        return 0x00;            /* 0 */
    return ~FbIntDiv(b, a);     /* 1 - b/a */
}

/* portion covered by both a and b */
static INLINE CARD8
fbCombineConjointInPart(CARD8 a, CARD8 b)
{
    /* min (1,b/a) */

    if (b >= a)                 /* b >= a -> b/a >= 1 */
        return 0xff;            /* 1 */
    return FbIntDiv(b, a);      /* b/a */
}

static FASTCALL void
fbCombineConjointGeneralU(CARD32 *dest, const CARD32 *src, int width,
                          CARD8 combine)
{
    int i;

    for (i = 0; i < width; ++i) {
        CARD32 s = src[i];

        CARD32 d = dest[i];

        CARD32 m, n, o, p;

        CARD16 Fa, Fb, t, u, v;

        CARD8 sa = s >> 24;

        CARD8 da = d >> 24;

        switch (combine & CombineA) {
        default:
            Fa = 0;
            break;
        case CombineAOut:
            Fa = fbCombineConjointOutPart(sa, da);
            break;
        case CombineAIn:
            Fa = fbCombineConjointInPart(sa, da);
            break;
        case CombineA:
            Fa = 0xff;
            break;
        }

        switch (combine & CombineB) {
        default:
            Fb = 0;
            break;
        case CombineBOut:
            Fb = fbCombineConjointOutPart(da, sa);
            break;
        case CombineBIn:
            Fb = fbCombineConjointInPart(da, sa);
            break;
        case CombineB:
            Fb = 0xff;
            break;
        }
        m = FbGen(s, d, 0, Fa, Fb, t, u, v);
        n = FbGen(s, d, 8, Fa, Fb, t, u, v);
        o = FbGen(s, d, 16, Fa, Fb, t, u, v);
        p = FbGen(s, d, 24, Fa, Fb, t, u, v);
        s = m | n | o | p;
        dest[i] = s;
    }
}

static FASTCALL void
fbCombineConjointOverU(CARD32 *dest, const CARD32 *src, int width)
{
    fbCombineConjointGeneralU(dest, src, width, CombineAOver);
}

static FASTCALL void
fbCombineConjointOverReverseU(CARD32 *dest, const CARD32 *src, int width)
{
    fbCombineConjointGeneralU(dest, src, width, CombineBOver);
}

static FASTCALL void
fbCombineConjointInU(CARD32 *dest, const CARD32 *src, int width)
{
    fbCombineConjointGeneralU(dest, src, width, CombineAIn);
}

static FASTCALL void
fbCombineConjointInReverseU(CARD32 *dest, const CARD32 *src, int width)
{
    fbCombineConjointGeneralU(dest, src, width, CombineBIn);
}

static FASTCALL void
fbCombineConjointOutU(CARD32 *dest, const CARD32 *src, int width)
{
    fbCombineConjointGeneralU(dest, src, width, CombineAOut);
}

static FASTCALL void
fbCombineConjointOutReverseU(CARD32 *dest, const CARD32 *src, int width)
{
    fbCombineConjointGeneralU(dest, src, width, CombineBOut);
}

static FASTCALL void
fbCombineConjointAtopU(CARD32 *dest, const CARD32 *src, int width)
{
    fbCombineConjointGeneralU(dest, src, width, CombineAAtop);
}

static FASTCALL void
fbCombineConjointAtopReverseU(CARD32 *dest, const CARD32 *src, int width)
{
    fbCombineConjointGeneralU(dest, src, width, CombineBAtop);
}

static FASTCALL void
fbCombineConjointXorU(CARD32 *dest, const CARD32 *src, int width)
{
    fbCombineConjointGeneralU(dest, src, width, CombineXor);
}

static const CombineFuncU fbCombineFuncU[] = {
    fbCombineClear,
    fbCombineSrcU,
    NULL,                       /* CombineDst */
    fbCombineOverU,
    fbCombineOverReverseU,
    fbCombineInU,
    fbCombineInReverseU,
    fbCombineOutU,
    fbCombineOutReverseU,
    fbCombineAtopU,
    fbCombineAtopReverseU,
    fbCombineXorU,
    fbCombineAddU,
    fbCombineSaturateU,
    NULL,
    NULL,
    fbCombineClear,
    fbCombineSrcU,
    NULL,                       /* CombineDst */
    fbCombineDisjointOverU,
    fbCombineSaturateU,         /* DisjointOverReverse */
    fbCombineDisjointInU,
    fbCombineDisjointInReverseU,
    fbCombineDisjointOutU,
    fbCombineDisjointOutReverseU,
    fbCombineDisjointAtopU,
    fbCombineDisjointAtopReverseU,
    fbCombineDisjointXorU,
    NULL,
    NULL,
    NULL,
    NULL,
    fbCombineClear,
    fbCombineSrcU,
    NULL,                       /* CombineDst */
    fbCombineConjointOverU,
    fbCombineConjointOverReverseU,
    fbCombineConjointInU,
    fbCombineConjointInReverseU,
    fbCombineConjointOutU,
    fbCombineConjointOutReverseU,
    fbCombineConjointAtopU,
    fbCombineConjointAtopReverseU,
    fbCombineConjointXorU,
};

static FASTCALL void
fbCombineMaskC(CARD32 *src, CARD32 *mask, int width)
{
    int i;

    for (i = 0; i < width; ++i) {
        CARD32 a = mask[i];

        CARD32 x;

        CARD16 xa;

        if (!a) {
            src[i] = 0;
            continue;
        }

        x = src[i];
        if (a == 0xffffffff) {
            x = x >> 24;
            x |= x << 8;
            x |= x << 16;
            mask[i] = x;
            continue;
        }

        xa = x >> 24;
        FbByteMulC(x, a);
        src[i] = x;
        FbByteMul(a, xa);
        mask[i] = a;
    }
}

static FASTCALL void
fbCombineMaskValueC(CARD32 *src, const CARD32 *mask, int width)
{
    int i;

    for (i = 0; i < width; ++i) {
        CARD32 a = mask[i];

        CARD32 x;

        if (!a) {
            src[i] = 0;
            continue;
        }

        if (a == 0xffffffff)
            continue;

        x = src[i];
        FbByteMulC(x, a);
        src[i] = x;
    }
}

static FASTCALL void
fbCombineMaskAlphaC(const CARD32 *src, CARD32 *mask, int width)
{
    int i;

    for (i = 0; i < width; ++i) {
        CARD32 a = mask[i];

        CARD32 x;

        if (!a)
            continue;

        x = src[i] >> 24;
        if (x == 0xff)
            continue;
        if (a == 0xffffffff) {
            x = x >> 24;
            x |= x << 8;
            x |= x << 16;
            mask[i] = x;
            continue;
        }

        FbByteMul(a, x);
        mask[i] = a;
    }
}

static FASTCALL void
fbCombineClearC(CARD32 *dest, CARD32 *src, CARD32 *mask, int width)
{
    memset(dest, 0, width * sizeof(CARD32));
}

static FASTCALL void
fbCombineSrcC(CARD32 *dest, CARD32 *src, CARD32 *mask, int width)
{
    fbCombineMaskValueC(src, mask, width);
    memcpy(dest, src, width * sizeof(CARD32));
}

static FASTCALL void
fbCombineOverC(CARD32 *dest, CARD32 *src, CARD32 *mask, int width)
{
    int i;

    fbCombineMaskC(src, mask, width);
    for (i = 0; i < width; ++i) {
        CARD32 s = src[i];

        CARD32 a = ~mask[i];

        if (a != 0xffffffff) {
            if (a) {
                CARD32 d = dest[i];

                FbByteMulAddC(d, a, s);
                s = d;
            }
            dest[i] = s;
        }
    }
}

static FASTCALL void
fbCombineOverReverseC(CARD32 *dest, CARD32 *src, CARD32 *mask, int width)
{
    int i;

    fbCombineMaskValueC(src, mask, width);
    for (i = 0; i < width; ++i) {
        CARD32 d = dest[i];

        CARD32 a = ~d >> 24;

        if (a) {
            CARD32 s = src[i];

            if (a != 0xff) {
                FbByteMulAdd(s, a, d);
            }
            dest[i] = s;
        }
    }
}

static FASTCALL void
fbCombineInC(CARD32 *dest, CARD32 *src, CARD32 *mask, int width)
{
    int i;

    fbCombineMaskValueC(src, mask, width);
    for (i = 0; i < width; ++i) {
        CARD32 d = dest[i];

        CARD16 a = d >> 24;

        CARD32 s = 0;

        if (a) {
            s = src[i];
            if (a != 0xff) {
                FbByteMul(s, a);
            }
        }
        dest[i] = s;
    }
}

static FASTCALL void
fbCombineInReverseC(CARD32 *dest, CARD32 *src, CARD32 *mask, int width)
{
    int i;

    fbCombineMaskAlphaC(src, mask, width);
    for (i = 0; i < width; ++i) {
        CARD32 a = mask[i];

        if (a != 0xffffffff) {
            CARD32 d = 0;

            if (a) {
                d = dest[i];
                FbByteMulC(d, a);
            }
            dest[i] = d;
        }
    }
}

static FASTCALL void
fbCombineOutC(CARD32 *dest, CARD32 *src, CARD32 *mask, int width)
{
    int i;

    fbCombineMaskValueC(src, mask, width);
    for (i = 0; i < width; ++i) {
        CARD32 d = dest[i];

        CARD16 a = ~d >> 24;

        CARD32 s = 0;

        if (a) {
            s = src[i];
            if (a != 0xff) {
                FbByteMul(s, a);
            }
        }
        dest[i] = s;
    }
}

static FASTCALL void
fbCombineOutReverseC(CARD32 *dest, CARD32 *src, CARD32 *mask, int width)
{
    int i;

    fbCombineMaskAlphaC(src, mask, width);
    for (i = 0; i < width; ++i) {
        CARD32 a = ~mask[i];

        if (a != 0xffffffff) {
            CARD32 d = 0;

            if (a) {
                d = dest[i];
                FbByteMulC(d, a);
            }
            dest[i] = d;
        }
    }
}

static FASTCALL void
fbCombineAtopC(CARD32 *dest, CARD32 *src, CARD32 *mask, int width)
{
    int i;

    fbCombineMaskC(src, mask, width);
    for (i = 0; i < width; ++i) {
        CARD32 d = dest[i];

        CARD32 s = src[i];

        CARD32 ad = ~mask[i];

        CARD16 as = d >> 24;

        FbByteAddMulC(d, ad, s, as);
        dest[i] = d;
    }
}

static FASTCALL void
fbCombineAtopReverseC(CARD32 *dest, CARD32 *src, CARD32 *mask, int width)
{
    int i;

    fbCombineMaskC(src, mask, width);
    for (i = 0; i < width; ++i) {

        CARD32 d = dest[i];

        CARD32 s = src[i];

        CARD32 ad = mask[i];

        CARD16 as = ~d >> 24;

        FbByteAddMulC(d, ad, s, as);
        dest[i] = d;
    }
}

static FASTCALL void
fbCombineXorC(CARD32 *dest, CARD32 *src, CARD32 *mask, int width)
{
    int i;

    fbCombineMaskC(src, mask, width);
    for (i = 0; i < width; ++i) {
        CARD32 d = dest[i];

        CARD32 s = src[i];

        CARD32 ad = ~mask[i];

        CARD16 as = ~d >> 24;

        FbByteAddMulC(d, ad, s, as);
        dest[i] = d;
    }
}

static FASTCALL void
fbCombineAddC(CARD32 *dest, CARD32 *src, CARD32 *mask, int width)
{
    int i;

    fbCombineMaskValueC(src, mask, width);
    for (i = 0; i < width; ++i) {
        CARD32 s = src[i];

        CARD32 d = dest[i];

        FbByteAdd(d, s);
        dest[i] = d;
    }
}

static FASTCALL void
fbCombineSaturateC(CARD32 *dest, CARD32 *src, CARD32 *mask, int width)
{
    int i;

    fbCombineMaskC(src, mask, width);
    for (i = 0; i < width; ++i) {
        CARD32 s, d;

        CARD16 sa, sr, sg, sb, da;

        CARD16 t, u, v;

        CARD32 m, n, o, p;

        d = dest[i];
        s = src[i];
        sa = (mask[i] >> 24);
        sr = (mask[i] >> 16) & 0xff;
        sg = (mask[i] >> 8) & 0xff;
        sb = (mask[i]) & 0xff;
        da = ~d >> 24;

        if (sb <= da)
            m = FbAdd(s, d, 0, t);
        else
            m = FbGen(s, d, 0, (da << 8) / sb, 0xff, t, u, v);

        if (sg <= da)
            n = FbAdd(s, d, 8, t);
        else
            n = FbGen(s, d, 8, (da << 8) / sg, 0xff, t, u, v);

        if (sr <= da)
            o = FbAdd(s, d, 16, t);
        else
            o = FbGen(s, d, 16, (da << 8) / sr, 0xff, t, u, v);

        if (sa <= da)
            p = FbAdd(s, d, 24, t);
        else
            p = FbGen(s, d, 24, (da << 8) / sa, 0xff, t, u, v);

        dest[i] = m | n | o | p;
    }
}

static FASTCALL void
fbCombineDisjointGeneralC(CARD32 *dest, CARD32 *src, CARD32 *mask, int width,
                          CARD8 combine)
{
    int i;

    fbCombineMaskC(src, mask, width);
    for (i = 0; i < width; ++i) {
        CARD32 s, d;

        CARD32 m, n, o, p;

        CARD32 Fa, Fb;

        CARD16 t, u, v;

        CARD32 sa;

        CARD8 da;

        s = src[i];
        sa = mask[i];
        d = dest[i];
        da = d >> 24;

        switch (combine & CombineA) {
        default:
            Fa = 0;
            break;
        case CombineAOut:
            m = fbCombineDisjointOutPart((CARD8) (sa >> 0), da);
            n = fbCombineDisjointOutPart((CARD8) (sa >> 8), da) << 8;
            o = fbCombineDisjointOutPart((CARD8) (sa >> 16), da) << 16;
            p = fbCombineDisjointOutPart((CARD8) (sa >> 24), da) << 24;
            Fa = m | n | o | p;
            break;
        case CombineAIn:
            m = fbCombineDisjointInPart((CARD8) (sa >> 0), da);
            n = fbCombineDisjointInPart((CARD8) (sa >> 8), da) << 8;
            o = fbCombineDisjointInPart((CARD8) (sa >> 16), da) << 16;
            p = fbCombineDisjointInPart((CARD8) (sa >> 24), da) << 24;
            Fa = m | n | o | p;
            break;
        case CombineA:
            Fa = 0xffffffff;
            break;
        }

        switch (combine & CombineB) {
        default:
            Fb = 0;
            break;
        case CombineBOut:
            m = fbCombineDisjointOutPart(da, (CARD8) (sa >> 0));
            n = fbCombineDisjointOutPart(da, (CARD8) (sa >> 8)) << 8;
            o = fbCombineDisjointOutPart(da, (CARD8) (sa >> 16)) << 16;
            p = fbCombineDisjointOutPart(da, (CARD8) (sa >> 24)) << 24;
            Fb = m | n | o | p;
            break;
        case CombineBIn:
            m = fbCombineDisjointInPart(da, (CARD8) (sa >> 0));
            n = fbCombineDisjointInPart(da, (CARD8) (sa >> 8)) << 8;
            o = fbCombineDisjointInPart(da, (CARD8) (sa >> 16)) << 16;
            p = fbCombineDisjointInPart(da, (CARD8) (sa >> 24)) << 24;
            Fb = m | n | o | p;
            break;
        case CombineB:
            Fb = 0xffffffff;
            break;
        }
        m = FbGen(s, d, 0, FbGet8(Fa, 0), FbGet8(Fb, 0), t, u, v);
        n = FbGen(s, d, 8, FbGet8(Fa, 8), FbGet8(Fb, 8), t, u, v);
        o = FbGen(s, d, 16, FbGet8(Fa, 16), FbGet8(Fb, 16), t, u, v);
        p = FbGen(s, d, 24, FbGet8(Fa, 24), FbGet8(Fb, 24), t, u, v);
        s = m | n | o | p;
        dest[i] = s;
    }
}

static FASTCALL void
fbCombineDisjointOverC(CARD32 *dest, CARD32 *src, CARD32 *mask, int width)
{
    fbCombineDisjointGeneralC(dest, src, mask, width, CombineAOver);
}

static FASTCALL void
fbCombineDisjointInC(CARD32 *dest, CARD32 *src, CARD32 *mask, int width)
{
    fbCombineDisjointGeneralC(dest, src, mask, width, CombineAIn);
}

static FASTCALL void
fbCombineDisjointInReverseC(CARD32 *dest, CARD32 *src, CARD32 *mask, int width)
{
    fbCombineDisjointGeneralC(dest, src, mask, width, CombineBIn);
}

static FASTCALL void
fbCombineDisjointOutC(CARD32 *dest, CARD32 *src, CARD32 *mask, int width)
{
    fbCombineDisjointGeneralC(dest, src, mask, width, CombineAOut);
}

static FASTCALL void
fbCombineDisjointOutReverseC(CARD32 *dest, CARD32 *src, CARD32 *mask, int width)
{
    fbCombineDisjointGeneralC(dest, src, mask, width, CombineBOut);
}

static FASTCALL void
fbCombineDisjointAtopC(CARD32 *dest, CARD32 *src, CARD32 *mask, int width)
{
    fbCombineDisjointGeneralC(dest, src, mask, width, CombineAAtop);
}

static FASTCALL void
fbCombineDisjointAtopReverseC(CARD32 *dest, CARD32 *src, CARD32 *mask,
                              int width)
{
    fbCombineDisjointGeneralC(dest, src, mask, width, CombineBAtop);
}

static FASTCALL void
fbCombineDisjointXorC(CARD32 *dest, CARD32 *src, CARD32 *mask, int width)
{
    fbCombineDisjointGeneralC(dest, src, mask, width, CombineXor);
}

static FASTCALL void
fbCombineConjointGeneralC(CARD32 *dest, CARD32 *src, CARD32 *mask, int width,
                          CARD8 combine)
{
    int i;

    fbCombineMaskC(src, mask, width);
    for (i = 0; i < width; ++i) {
        CARD32 s, d;

        CARD32 m, n, o, p;

        CARD32 Fa, Fb;

        CARD16 t, u, v;

        CARD32 sa;

        CARD8 da;

        s = src[i];
        sa = mask[i];
        d = dest[i];
        da = d >> 24;

        switch (combine & CombineA) {
        default:
            Fa = 0;
            break;
        case CombineAOut:
            m = fbCombineConjointOutPart((CARD8) (sa >> 0), da);
            n = fbCombineConjointOutPart((CARD8) (sa >> 8), da) << 8;
            o = fbCombineConjointOutPart((CARD8) (sa >> 16), da) << 16;
            p = fbCombineConjointOutPart((CARD8) (sa >> 24), da) << 24;
            Fa = m | n | o | p;
            break;
        case CombineAIn:
            m = fbCombineConjointInPart((CARD8) (sa >> 0), da);
            n = fbCombineConjointInPart((CARD8) (sa >> 8), da) << 8;
            o = fbCombineConjointInPart((CARD8) (sa >> 16), da) << 16;
            p = fbCombineConjointInPart((CARD8) (sa >> 24), da) << 24;
            Fa = m | n | o | p;
            break;
        case CombineA:
            Fa = 0xffffffff;
            break;
        }

        switch (combine & CombineB) {
        default:
            Fb = 0;
            break;
        case CombineBOut:
            m = fbCombineConjointOutPart(da, (CARD8) (sa >> 0));
            n = fbCombineConjointOutPart(da, (CARD8) (sa >> 8)) << 8;
            o = fbCombineConjointOutPart(da, (CARD8) (sa >> 16)) << 16;
            p = fbCombineConjointOutPart(da, (CARD8) (sa >> 24)) << 24;
            Fb = m | n | o | p;
            break;
        case CombineBIn:
            m = fbCombineConjointInPart(da, (CARD8) (sa >> 0));
            n = fbCombineConjointInPart(da, (CARD8) (sa >> 8)) << 8;
            o = fbCombineConjointInPart(da, (CARD8) (sa >> 16)) << 16;
            p = fbCombineConjointInPart(da, (CARD8) (sa >> 24)) << 24;
            Fb = m | n | o | p;
            break;
        case CombineB:
            Fb = 0xffffffff;
            break;
        }
        m = FbGen(s, d, 0, FbGet8(Fa, 0), FbGet8(Fb, 0), t, u, v);
        n = FbGen(s, d, 8, FbGet8(Fa, 8), FbGet8(Fb, 8), t, u, v);
        o = FbGen(s, d, 16, FbGet8(Fa, 16), FbGet8(Fb, 16), t, u, v);
        p = FbGen(s, d, 24, FbGet8(Fa, 24), FbGet8(Fb, 24), t, u, v);
        s = m | n | o | p;
        dest[i] = s;
    }
}

static FASTCALL void
fbCombineConjointOverC(CARD32 *dest, CARD32 *src, CARD32 *mask, int width)
{
    fbCombineConjointGeneralC(dest, src, mask, width, CombineAOver);
}

static FASTCALL void
fbCombineConjointOverReverseC(CARD32 *dest, CARD32 *src, CARD32 *mask,
                              int width)
{
    fbCombineConjointGeneralC(dest, src, mask, width, CombineBOver);
}

static FASTCALL void
fbCombineConjointInC(CARD32 *dest, CARD32 *src, CARD32 *mask, int width)
{
    fbCombineConjointGeneralC(dest, src, mask, width, CombineAIn);
}

static FASTCALL void
fbCombineConjointInReverseC(CARD32 *dest, CARD32 *src, CARD32 *mask, int width)
{
    fbCombineConjointGeneralC(dest, src, mask, width, CombineBIn);
}

static FASTCALL void
fbCombineConjointOutC(CARD32 *dest, CARD32 *src, CARD32 *mask, int width)
{
    fbCombineConjointGeneralC(dest, src, mask, width, CombineAOut);
}

static FASTCALL void
fbCombineConjointOutReverseC(CARD32 *dest, CARD32 *src, CARD32 *mask, int width)
{
    fbCombineConjointGeneralC(dest, src, mask, width, CombineBOut);
}

static FASTCALL void
fbCombineConjointAtopC(CARD32 *dest, CARD32 *src, CARD32 *mask, int width)
{
    fbCombineConjointGeneralC(dest, src, mask, width, CombineAAtop);
}

static FASTCALL void
fbCombineConjointAtopReverseC(CARD32 *dest, CARD32 *src, CARD32 *mask,
                              int width)
{
    fbCombineConjointGeneralC(dest, src, mask, width, CombineBAtop);
}

static FASTCALL void
fbCombineConjointXorC(CARD32 *dest, CARD32 *src, CARD32 *mask, int width)
{
    fbCombineConjointGeneralC(dest, src, mask, width, CombineXor);
}

static const CombineFuncC fbCombineFuncC[] = {
    fbCombineClearC,
    fbCombineSrcC,
    NULL,                       /* Dest */
    fbCombineOverC,
    fbCombineOverReverseC,
    fbCombineInC,
    fbCombineInReverseC,
    fbCombineOutC,
    fbCombineOutReverseC,
    fbCombineAtopC,
    fbCombineAtopReverseC,
    fbCombineXorC,
    fbCombineAddC,
    fbCombineSaturateC,
    NULL,
    NULL,
    fbCombineClearC,            /* 0x10 */
    fbCombineSrcC,
    NULL,                       /* Dest */
    fbCombineDisjointOverC,
    fbCombineSaturateC,         /* DisjointOverReverse */
    fbCombineDisjointInC,
    fbCombineDisjointInReverseC,
    fbCombineDisjointOutC,
    fbCombineDisjointOutReverseC,
    fbCombineDisjointAtopC,
    fbCombineDisjointAtopReverseC,
    fbCombineDisjointXorC,      /* 0x1b */
    NULL,
    NULL,
    NULL,
    NULL,
    fbCombineClearC,
    fbCombineSrcC,
    NULL,                       /* Dest */
    fbCombineConjointOverC,
    fbCombineConjointOverReverseC,
    fbCombineConjointInC,
    fbCombineConjointInReverseC,
    fbCombineConjointOutC,
    fbCombineConjointOutReverseC,
    fbCombineConjointAtopC,
    fbCombineConjointAtopReverseC,
    fbCombineConjointXorC,
};

static const FbComposeFunctions composeFunctions = {
    fbCombineFuncU,
    fbCombineFuncC,
    fbCombineMaskU
};

static void
fbFetchSolid(PicturePtr pict, int x, int y, int width, CARD32 *buffer)
{
    FbBits *bits;

    FbStride stride;

    int bpp;

    int xoff, yoff;

    CARD32 color;

    CARD32 *end;

    fetchPixelProc fetch = fetchPixelProcForPicture(pict);

    miIndexedPtr indexed = (miIndexedPtr) pict->pFormat->index.devPrivate;

    fbGetDrawable(pict->pDrawable, bits, stride, bpp, xoff, yoff);
    bits += yoff * stride + (xoff * bpp >> FB_SHIFT);

    color = fetch(bits, 0, indexed);

    end = buffer + width;
    while (buffer < end)
        *buffer++ = color;
}

static void
fbFetch(PicturePtr pict, int x, int y, int width, CARD32 *buffer)
{
    FbBits *bits;

    FbStride stride;

    int bpp;

    int xoff, yoff;

    fetchProc fetch = fetchProcForPicture(pict);

    miIndexedPtr indexed = (miIndexedPtr) pict->pFormat->index.devPrivate;

    fbGetDrawable(pict->pDrawable, bits, stride, bpp, xoff, yoff);
    x += xoff;
    y += yoff;

    bits += y * stride;

    fetch(bits, x, width, buffer, indexed);
}

#define MOD(a,b) ((a) < 0 ? ((b) - ((-(a) - 1) % (b))) - 1 : (a) % (b))
#define DIV(a,b) ((((a) < 0) == ((b) < 0)) ? (a) / (b) :\
        ((a) - (b) + 1 - (((b) < 0) << 1)) / (b))

static CARD32
gradientPixel(const SourcePictPtr pGradient, xFixed_48_16 pos,
              unsigned int spread)
{
    int ipos = (pos * PICT_GRADIENT_STOPTABLE_SIZE - 1) >> 16;

    /* calculate the actual offset. */
    if (ipos < 0 || ipos >= PICT_GRADIENT_STOPTABLE_SIZE) {
        if (pGradient->type == SourcePictTypeConical || spread == RepeatNormal) {
            ipos = ipos % PICT_GRADIENT_STOPTABLE_SIZE;
            ipos = ipos < 0 ? PICT_GRADIENT_STOPTABLE_SIZE + ipos : ipos;

        }
        else if (spread == RepeatReflect) {
            const int limit = PICT_GRADIENT_STOPTABLE_SIZE * 2 - 1;

            ipos = ipos % limit;
            ipos = ipos < 0 ? limit + ipos : ipos;
            ipos = ipos >= PICT_GRADIENT_STOPTABLE_SIZE ? limit - ipos : ipos;

        }
        else if (spread == RepeatPad) {
            if (ipos < 0)
                ipos = 0;
            else if (ipos >= PICT_GRADIENT_STOPTABLE_SIZE)
                ipos = PICT_GRADIENT_STOPTABLE_SIZE - 1;
        }
        else {                  /* RepeatNone */
            return 0;
        }
    }

    assert(ipos >= 0);
    assert(ipos < PICT_GRADIENT_STOPTABLE_SIZE);

    return pGradient->linear.colorTable[ipos];
}

static void
fbFetchSourcePict(PicturePtr pict, int x, int y, int width, CARD32 *buffer)
{
    SourcePictPtr pGradient = pict->pSourcePict;

    CARD32 *end = buffer + width;

    if (pGradient->type == SourcePictTypeSolidFill) {
        CARD32 color = pGradient->solidFill.color;

        while (buffer < end) {
            *buffer++ = color;
        }
    }
    else if (pGradient->type == SourcePictTypeLinear) {
        PictVector v, unit;

        xFixed_32_32 l;

        xFixed_48_16 dx, dy, a, b, off;

        /* reference point is the center of the pixel */
        v.vector[0] = IntToxFixed(x) + xFixed1 / 2;
        v.vector[1] = IntToxFixed(y) + xFixed1 / 2;
        v.vector[2] = xFixed1;
        if (pict->transform) {
            if (!PictureTransformPoint3d(pict->transform, &v))
                return;
            unit.vector[0] = pict->transform->matrix[0][0];
            unit.vector[1] = pict->transform->matrix[1][0];
            unit.vector[2] = pict->transform->matrix[2][0];
        }
        else {
            unit.vector[0] = xFixed1;
            unit.vector[1] = 0;
            unit.vector[2] = 0;
        }

        dx = pGradient->linear.p2.x - pGradient->linear.p1.x;
        dy = pGradient->linear.p2.y - pGradient->linear.p1.y;
        l = dx * dx + dy * dy;
        if (l != 0) {
            a = (dx << 32) / l;
            b = (dy << 32) / l;
            off =
                (-a * pGradient->linear.p1.x -
                 b * pGradient->linear.p1.y) >> 16;
        }
        if (l == 0 || (unit.vector[2] == 0 && v.vector[2] == xFixed1)) {
            xFixed_48_16 inc, t;

            /* affine transformation only */
            if (l == 0) {
                t = 0;
                inc = 0;
            }
            else {
                t = ((a * v.vector[0] + b * v.vector[1]) >> 16) + off;
                inc = (a * unit.vector[0] + b * unit.vector[1]) >> 16;
            }
            while (buffer < end) {
                *buffer++ = gradientPixel(pGradient, t, pict->repeatType);
                t += inc;
            }
        }
        else {
            /* projective transformation */
            while (buffer < end) {
                xFixed_48_16 t;

                if (v.vector[2] == 0) {
                    t = 0;
                }
                else {
                    xFixed_48_16 x, y;

                    x = ((xFixed_48_16) v.vector[0] << 16) / v.vector[2];
                    y = ((xFixed_48_16) v.vector[1] << 16) / v.vector[2];
                    t = ((a * x + b * y) >> 16) + off;
                }
                *buffer++ = gradientPixel(pGradient, t, pict->repeatType);
                v.vector[0] += unit.vector[0];
                v.vector[1] += unit.vector[1];
                v.vector[2] += unit.vector[2];
            }
        }
    }
    else {
        /* radial or conical */
        Bool affine = TRUE;

        double cx = 1.;

        double cy = 0.;

        double cz = 0.;

        double rx = x;

        double ry = y;

        double rz = 1.;

        if (pict->transform) {
            PictVector v;

            /* reference point is the center of the pixel */
            v.vector[0] = IntToxFixed(x) + xFixed1 / 2;
            v.vector[1] = IntToxFixed(y) + xFixed1 / 2;
            v.vector[2] = xFixed1;
            if (!PictureTransformPoint3d(pict->transform, &v))
                return;

            cx = pict->transform->matrix[0][0] / 65536.;
            cy = pict->transform->matrix[1][0] / 65536.;
            cz = pict->transform->matrix[2][0] / 65536.;
            rx = v.vector[0] / 65536.;
            ry = v.vector[1] / 65536.;
            rz = v.vector[2] / 65536.;
            affine = pict->transform->matrix[2][0] == 0 &&
                v.vector[2] == xFixed1;
        }

        if (pGradient->type == SourcePictTypeRadial) {
            if (affine) {
                rx -= pGradient->radial.fx;
                ry -= pGradient->radial.fy;

                while (buffer < end) {
                    double b =
                        2 * (rx * pGradient->radial.dx +
                             ry * pGradient->radial.dy);
                    double c = -(rx * rx + ry * ry);

                    double det = (b * b) - (4 * pGradient->radial.a * c);

                    double s = (-b + sqrt(det)) / (2. * pGradient->radial.a);

                    *buffer = gradientPixel(pGradient,
                                            (xFixed_48_16) ((s *
                                                             pGradient->radial.
                                                             m +
                                                             pGradient->radial.
                                                             b) * 65536),
                                            pict->repeatType);
                    ++buffer;
                    rx += cx;
                    ry += cy;
                }
            }
            else {
                while (buffer < end) {
                    double x, y;

                    double b, c, det, s;

                    if (rz != 0) {
                        x = rx / rz;
                        y = ry / rz;
                    }
                    else {
                        x = y = 0.;
                    }
                    x -= pGradient->radial.fx;
                    y -= pGradient->radial.fy;
                    b = 2 * (x * pGradient->radial.dx +
                             y * pGradient->radial.dy);
                    c = -(x * x + y * y);
                    det = (b * b) - (4 * pGradient->radial.a * c);
                    s = (-b + sqrt(det)) / (2. * pGradient->radial.a);
                    *buffer = gradientPixel(pGradient,
                                            (xFixed_48_16) ((s *
                                                             pGradient->radial.
                                                             m +
                                                             pGradient->radial.
                                                             b) * 65536),
                                            pict->repeatType);
                    ++buffer;
                    rx += cx;
                    ry += cy;
                    rz += cz;
                }
            }
        }
        else {                  /* SourcePictTypeConical */

            double a = pGradient->conical.angle / (180. * 65536);

            if (affine) {
                rx -= pGradient->conical.center.x / 65536.;
                ry -= pGradient->conical.center.y / 65536.;

                while (buffer < end) {
                    double angle = atan2(ry, rx) + a;

                    *buffer =
                        gradientPixel(pGradient,
                                      (xFixed_48_16) (angle *
                                                      (65536. / (2 * M_PI))),
                                      pict->repeatType);
                    ++buffer;
                    rx += cx;
                    ry += cy;
                }
            }
            else {

                while (buffer < end) {
                    double x, y, angle;

                    if (rz != 0) {
                        x = rx / rz;
                        y = ry / rz;
                    }
                    else {
                        x = y = 0.;
                    }
                    x -= pGradient->conical.center.x / 65536.;
                    y -= pGradient->conical.center.y / 65536.;
                    angle = atan2(y, x) + a;
                    *buffer =
                        gradientPixel(pGradient,
                                      (xFixed_48_16) (angle *
                                                      (65536. / (2 * M_PI))),
                                      pict->repeatType);
                    ++buffer;
                    rx += cx;
                    ry += cy;
                    rz += cz;
                }
            }
        }
    }
}

static void
fbFetchTransformed(PicturePtr pict, int x, int y, int width, CARD32 *buffer)
{
    FbBits *bits;

    FbStride stride;

    int bpp;

    int xoff, yoff, dx, dy;

    fetchPixelProc fetch;

    PictVector v;

    PictVector unit;

    int i;

    BoxRec box;

    miIndexedPtr indexed = (miIndexedPtr) pict->pFormat->index.devPrivate;

    Bool affine = TRUE;

    fetch = fetchPixelProcForPicture(pict);

    fbGetDrawable(pict->pDrawable, bits, stride, bpp, xoff, yoff);
    x += xoff;
    y += yoff;

    dx = pict->pDrawable->x;
    dy = pict->pDrawable->y;

    /* reference point is the center of the pixel */
    v.vector[0] = IntToxFixed(x - dx) + xFixed1 / 2;
    v.vector[1] = IntToxFixed(y - dy) + xFixed1 / 2;
    v.vector[2] = xFixed1;

    /* when using convolution filters one might get here without a transform */
    if (pict->transform) {
        if (!PictureTransformPoint3d(pict->transform, &v))
            return;
        unit.vector[0] = pict->transform->matrix[0][0];
        unit.vector[1] = pict->transform->matrix[1][0];
        unit.vector[2] = pict->transform->matrix[2][0];
        affine = v.vector[2] == xFixed1 && unit.vector[2] == 0;
    }
    else {
        unit.vector[0] = xFixed1;
        unit.vector[1] = 0;
        unit.vector[2] = 0;
    }

    if (pict->filter == PictFilterNearest) {
        if (pict->repeatType == RepeatNormal) {
            if (REGION_NUM_RECTS(pict->pCompositeClip) == 1) {
                for (i = 0; i < width; ++i) {
                    if (!v.vector[2]) {
                        buffer[i] = 0;
                    }
                    else {
                        if (!affine) {
                            y = MOD(DIV(v.vector[1], v.vector[2]),
                                    pict->pDrawable->height);
                            x = MOD(DIV(v.vector[0], v.vector[2]),
                                    pict->pDrawable->width);
                        }
                        else {
                            y = MOD(v.vector[1] >> 16, pict->pDrawable->height);
                            x = MOD(v.vector[0] >> 16, pict->pDrawable->width);
                        }
                        buffer[i] =
                            fetch(bits + (y + dy) * stride, x + dx, indexed);
                    }
                    v.vector[0] += unit.vector[0];
                    v.vector[1] += unit.vector[1];
                    v.vector[2] += unit.vector[2];
                }
            }
            else {
                for (i = 0; i < width; ++i) {
                    if (!v.vector[2]) {
                        buffer[i] = 0;
                    }
                    else {
                        if (!affine) {
                            y = MOD(DIV(v.vector[1], v.vector[2]),
                                    pict->pDrawable->height);
                            x = MOD(DIV(v.vector[0], v.vector[2]),
                                    pict->pDrawable->width);
                        }
                        else {
                            y = MOD(v.vector[1] >> 16, pict->pDrawable->height);
                            x = MOD(v.vector[0] >> 16, pict->pDrawable->width);
                        }
                        if (POINT_IN_REGION(pict->pCompositeClip, x + dx, y + dy, &box))
                            buffer[i] =
                                fetch(bits + (y + dy) * stride, x + dx,
                                      indexed);
                        else
                            buffer[i] = 0;
                    }
                    v.vector[0] += unit.vector[0];
                    v.vector[1] += unit.vector[1];
                    v.vector[2] += unit.vector[2];
                }
            }
        }
        else {
            if (REGION_NUM_RECTS(pict->pCompositeClip) == 1) {
                box = pict->pCompositeClip->extents;
                for (i = 0; i < width; ++i) {
                    if (!v.vector[2]) {
                        buffer[i] = 0;
                    }
                    else {
                        if (!affine) {
                            y = DIV(v.vector[1], v.vector[2]);
                            x = DIV(v.vector[0], v.vector[2]);
                        }
                        else {
                            y = v.vector[1] >> 16;
                            x = v.vector[0] >> 16;
                        }
                        buffer[i] =
                            ((x < box.x1 - dx) | (x >= box.x2 - dx) | (y <
                                                                       box.y1 -
                                                                       dy) | (y
                                                                              >=
                                                                              box.
                                                                              y2
                                                                              -
                                                                              dy))
                            ? 0 : fetch(bits + (y + dy) * stride, x + dx,
                                        indexed);
                    }
                    v.vector[0] += unit.vector[0];
                    v.vector[1] += unit.vector[1];
                    v.vector[2] += unit.vector[2];
                }
            }
            else {
                for (i = 0; i < width; ++i) {
                    if (!v.vector[2]) {
                        buffer[i] = 0;
                    }
                    else {
                        if (!affine) {
                            y = DIV(v.vector[1], v.vector[2]);
                            x = DIV(v.vector[0], v.vector[2]);
                        }
                        else {
                            y = v.vector[1] >> 16;
                            x = v.vector[0] >> 16;
                        }
                        if (POINT_IN_REGION(pict->pCompositeClip, x + dx, y + dy, &box))
                            buffer[i] =
                                fetch(bits + (y + dy) * stride, x + dx,
                                      indexed);
                        else
                            buffer[i] = 0;
                    }
                    v.vector[0] += unit.vector[0];
                    v.vector[1] += unit.vector[1];
                    v.vector[2] += unit.vector[2];
                }
            }
        }
    }
    else if (pict->filter == PictFilterBilinear) {
        /* adjust vector for maximum contribution at 0.5, 0.5 of each texel. */
        v.vector[0] -= v.vector[2] / 2;
        v.vector[1] -= v.vector[2] / 2;
        unit.vector[0] -= unit.vector[2] / 2;
        unit.vector[1] -= unit.vector[2] / 2;

        if (pict->repeatType == RepeatNormal) {
            if (REGION_NUM_RECTS(pict->pCompositeClip) == 1) {
                for (i = 0; i < width; ++i) {
                    if (!v.vector[2]) {
                        buffer[i] = 0;
                    }
                    else {
                        int x1, x2, y1, y2, distx, idistx, disty, idisty;

                        FbBits *b;

                        CARD32 tl, tr, bl, br, r;

                        CARD32 ft, fb;

                        if (!affine) {
                            xFixed_48_16 div;

                            div =
                                ((xFixed_48_16) v.vector[0] << 16) /
                                v.vector[2];
                            x1 = div >> 16;
                            distx = ((xFixed) div >> 8) & 0xff;
                            div =
                                ((xFixed_48_16) v.vector[1] << 16) /
                                v.vector[2];
                            y1 = div >> 16;
                            disty = ((xFixed) div >> 8) & 0xff;
                        }
                        else {
                            x1 = v.vector[0] >> 16;
                            distx = (v.vector[0] >> 8) & 0xff;
                            y1 = v.vector[1] >> 16;
                            disty = (v.vector[1] >> 8) & 0xff;
                        }
                        x2 = x1 + 1;
                        y2 = y1 + 1;

                        idistx = 256 - distx;
                        idisty = 256 - disty;

                        x1 = MOD(x1, pict->pDrawable->width);
                        x2 = MOD(x2, pict->pDrawable->width);
                        y1 = MOD(y1, pict->pDrawable->height);
                        y2 = MOD(y2, pict->pDrawable->height);

                        b = bits + (y1 + dy) * stride;

                        tl = fetch(b, x1 + dx, indexed);
                        tr = fetch(b, x2 + dx, indexed);
                        b = bits + (y2 + dy) * stride;
                        bl = fetch(b, x1 + dx, indexed);
                        br = fetch(b, x2 + dx, indexed);

                        ft = FbGet8(tl, 0) * idistx + FbGet8(tr, 0) * distx;
                        fb = FbGet8(bl, 0) * idistx + FbGet8(br, 0) * distx;
                        r = (((ft * idisty + fb * disty) >> 16) & 0xff);
                        ft = FbGet8(tl, 8) * idistx + FbGet8(tr, 8) * distx;
                        fb = FbGet8(bl, 8) * idistx + FbGet8(br, 8) * distx;
                        r |= (((ft * idisty + fb * disty) >> 8) & 0xff00);
                        ft = FbGet8(tl, 16) * idistx + FbGet8(tr, 16) * distx;
                        fb = FbGet8(bl, 16) * idistx + FbGet8(br, 16) * distx;
                        r |= (((ft * idisty + fb * disty)) & 0xff0000);
                        ft = FbGet8(tl, 24) * idistx + FbGet8(tr, 24) * distx;
                        fb = FbGet8(bl, 24) * idistx + FbGet8(br, 24) * distx;
                        r |= (((ft * idisty + fb * disty) << 8) & 0xff000000);
                        buffer[i] = r;
                    }
                    v.vector[0] += unit.vector[0];
                    v.vector[1] += unit.vector[1];
                    v.vector[2] += unit.vector[2];
                }
            }
            else {
                for (i = 0; i < width; ++i) {
                    if (!v.vector[2]) {
                        buffer[i] = 0;
                    }
                    else {
                        int x1, x2, y1, y2, distx, idistx, disty, idisty;

                        FbBits *b;

                        CARD32 tl, tr, bl, br, r;

                        CARD32 ft, fb;

                        if (!affine) {
                            xFixed_48_16 div;

                            div =
                                ((xFixed_48_16) v.vector[0] << 16) /
                                v.vector[2];
                            x1 = div >> 16;
                            distx = ((xFixed) div >> 8) & 0xff;
                            div =
                                ((xFixed_48_16) v.vector[1] << 16) /
                                v.vector[2];
                            y1 = div >> 16;
                            disty = ((xFixed) div >> 8) & 0xff;
                        }
                        else {
                            x1 = v.vector[0] >> 16;
                            distx = (v.vector[0] >> 8) & 0xff;
                            y1 = v.vector[1] >> 16;
                            disty = (v.vector[1] >> 8) & 0xff;
                        }
                        x2 = x1 + 1;
                        y2 = y1 + 1;

                        idistx = 256 - distx;
                        idisty = 256 - disty;

                        x1 = MOD(x1, pict->pDrawable->width);
                        x2 = MOD(x2, pict->pDrawable->width);
                        y1 = MOD(y1, pict->pDrawable->height);
                        y2 = MOD(y2, pict->pDrawable->height);

                        b = bits + (y1 + dy) * stride;

                        tl = POINT_IN_REGION(pict->pCompositeClip, x1 + dx,
                                             y1 + dy, &box)
                            ? fetch(b, x1 + dx, indexed) : 0;
                        tr = POINT_IN_REGION(pict->pCompositeClip, x2 + dx,
                                             y1 + dy, &box)
                            ? fetch(b, x2 + dx, indexed) : 0;
                        b = bits + (y2 + dy) * stride;
                        bl = POINT_IN_REGION(pict->pCompositeClip, x1 + dx,
                                             y2 + dy, &box)
                            ? fetch(b, x1 + dx, indexed) : 0;
                        br = POINT_IN_REGION(pict->pCompositeClip, x2 + dx,
                                             y2 + dy, &box)
                            ? fetch(b, x2 + dx, indexed) : 0;

                        ft = FbGet8(tl, 0) * idistx + FbGet8(tr, 0) * distx;
                        fb = FbGet8(bl, 0) * idistx + FbGet8(br, 0) * distx;
                        r = (((ft * idisty + fb * disty) >> 16) & 0xff);
                        ft = FbGet8(tl, 8) * idistx + FbGet8(tr, 8) * distx;
                        fb = FbGet8(bl, 8) * idistx + FbGet8(br, 8) * distx;
                        r |= (((ft * idisty + fb * disty) >> 8) & 0xff00);
                        ft = FbGet8(tl, 16) * idistx + FbGet8(tr, 16) * distx;
                        fb = FbGet8(bl, 16) * idistx + FbGet8(br, 16) * distx;
                        r |= (((ft * idisty + fb * disty)) & 0xff0000);
                        ft = FbGet8(tl, 24) * idistx + FbGet8(tr, 24) * distx;
                        fb = FbGet8(bl, 24) * idistx + FbGet8(br, 24) * distx;
                        r |= (((ft * idisty + fb * disty) << 8) & 0xff000000);
                        buffer[i] = r;
                    }
                    v.vector[0] += unit.vector[0];
                    v.vector[1] += unit.vector[1];
                    v.vector[2] += unit.vector[2];
                }
            }
        }
        else {
            if (REGION_NUM_RECTS(pict->pCompositeClip) == 1) {
                box = pict->pCompositeClip->extents;
                for (i = 0; i < width; ++i) {
                    if (!v.vector[2]) {
                        buffer[i] = 0;
                    }
                    else {
                        int x1, x2, y1, y2, distx, idistx, disty, idisty, x_off;

                        FbBits *b;

                        CARD32 tl, tr, bl, br, r;

                        Bool x1_out, x2_out, y1_out, y2_out;

                        CARD32 ft, fb;

                        if (!affine) {
                            xFixed_48_16 div;

                            div =
                                ((xFixed_48_16) v.vector[0] << 16) /
                                v.vector[2];
                            x1 = div >> 16;
                            distx = ((xFixed) div >> 8) & 0xff;
                            div =
                                ((xFixed_48_16) v.vector[1] << 16) /
                                v.vector[2];
                            y1 = div >> 16;
                            disty = ((xFixed) div >> 8) & 0xff;
                        }
                        else {
                            x1 = v.vector[0] >> 16;
                            distx = (v.vector[0] >> 8) & 0xff;
                            y1 = v.vector[1] >> 16;
                            disty = (v.vector[1] >> 8) & 0xff;
                        }
                        x2 = x1 + 1;
                        y2 = y1 + 1;

                        idistx = 256 - distx;
                        idisty = 256 - disty;

                        b = bits + (y1 + dy) * stride;
                        x_off = x1 + dx;

                        x1_out = (x1 < box.x1 - dx) | (x1 >= box.x2 - dx);
                        x2_out = (x2 < box.x1 - dx) | (x2 >= box.x2 - dx);
                        y1_out = (y1 < box.y1 - dy) | (y1 >= box.y2 - dy);
                        y2_out = (y2 < box.y1 - dy) | (y2 >= box.y2 - dy);

                        tl = x1_out | y1_out ? 0 : fetch(b, x_off, indexed);
                        tr = x2_out | y1_out ? 0 : fetch(b, x_off + 1, indexed);
                        b += stride;
                        bl = x1_out | y2_out ? 0 : fetch(b, x_off, indexed);
                        br = x2_out | y2_out ? 0 : fetch(b, x_off + 1, indexed);

                        ft = FbGet8(tl, 0) * idistx + FbGet8(tr, 0) * distx;
                        fb = FbGet8(bl, 0) * idistx + FbGet8(br, 0) * distx;
                        r = (((ft * idisty + fb * disty) >> 16) & 0xff);
                        ft = FbGet8(tl, 8) * idistx + FbGet8(tr, 8) * distx;
                        fb = FbGet8(bl, 8) * idistx + FbGet8(br, 8) * distx;
                        r |= (((ft * idisty + fb * disty) >> 8) & 0xff00);
                        ft = FbGet8(tl, 16) * idistx + FbGet8(tr, 16) * distx;
                        fb = FbGet8(bl, 16) * idistx + FbGet8(br, 16) * distx;
                        r |= (((ft * idisty + fb * disty)) & 0xff0000);
                        ft = FbGet8(tl, 24) * idistx + FbGet8(tr, 24) * distx;
                        fb = FbGet8(bl, 24) * idistx + FbGet8(br, 24) * distx;
                        r |= (((ft * idisty + fb * disty) << 8) & 0xff000000);
                        buffer[i] = r;
                    }
                    v.vector[0] += unit.vector[0];
                    v.vector[1] += unit.vector[1];
                    v.vector[2] += unit.vector[2];
                }
            }
            else {
                for (i = 0; i < width; ++i) {
                    if (!v.vector[2]) {
                        buffer[i] = 0;
                    }
                    else {
                        int x1, x2, y1, y2, distx, idistx, disty, idisty, x_off;

                        FbBits *b;

                        CARD32 tl, tr, bl, br, r;

                        CARD32 ft, fb;

                        if (!affine) {
                            xFixed_48_16 div;

                            div =
                                ((xFixed_48_16) v.vector[0] << 16) /
                                v.vector[2];
                            x1 = div >> 16;
                            distx = ((xFixed) div >> 8) & 0xff;
                            div =
                                ((xFixed_48_16) v.vector[1] << 16) /
                                v.vector[2];
                            y1 = div >> 16;
                            disty = ((xFixed) div >> 8) & 0xff;
                        }
                        else {
                            x1 = v.vector[0] >> 16;
                            distx = (v.vector[0] >> 8) & 0xff;
                            y1 = v.vector[1] >> 16;
                            disty = (v.vector[1] >> 8) & 0xff;
                        }
                        x2 = x1 + 1;
                        y2 = y1 + 1;

                        idistx = 256 - distx;
                        idisty = 256 - disty;

                        b = bits + (y1 + dy) * stride;
                        x_off = x1 + dx;

                        tl = POINT_IN_REGION(pict->pCompositeClip, x1 + dx,
                                             y1 + dy, &box)
                            ? fetch(b, x_off, indexed) : 0;
                        tr = POINT_IN_REGION(pict->pCompositeClip, x2 + dx,
                                             y1 + dy, &box)
                            ? fetch(b, x_off + 1, indexed) : 0;
                        b += stride;
                        bl = POINT_IN_REGION(pict->pCompositeClip, x1 + dx,
                                             y2 + dy, &box)
                            ? fetch(b, x_off, indexed) : 0;
                        br = POINT_IN_REGION(pict->pCompositeClip, x2 + dx,
                                             y2 + dy, &box)
                            ? fetch(b, x_off + 1, indexed) : 0;

                        ft = FbGet8(tl, 0) * idistx + FbGet8(tr, 0) * distx;
                        fb = FbGet8(bl, 0) * idistx + FbGet8(br, 0) * distx;
                        r = (((ft * idisty + fb * disty) >> 16) & 0xff);
                        ft = FbGet8(tl, 8) * idistx + FbGet8(tr, 8) * distx;
                        fb = FbGet8(bl, 8) * idistx + FbGet8(br, 8) * distx;
                        r |= (((ft * idisty + fb * disty) >> 8) & 0xff00);
                        ft = FbGet8(tl, 16) * idistx + FbGet8(tr, 16) * distx;
                        fb = FbGet8(bl, 16) * idistx + FbGet8(br, 16) * distx;
                        r |= (((ft * idisty + fb * disty)) & 0xff0000);
                        ft = FbGet8(tl, 24) * idistx + FbGet8(tr, 24) * distx;
                        fb = FbGet8(bl, 24) * idistx + FbGet8(br, 24) * distx;
                        r |= (((ft * idisty + fb * disty) << 8) & 0xff000000);
                        buffer[i] = r;
                    }
                    v.vector[0] += unit.vector[0];
                    v.vector[1] += unit.vector[1];
                    v.vector[2] += unit.vector[2];
                }
            }
        }
    }
    else if (pict->filter == PictFilterConvolution) {
        xFixed *params = pict->filter_params;

        INT32 cwidth = xFixedToInt(params[0]);

        INT32 cheight = xFixedToInt(params[1]);

        int xoff = (params[0] - xFixed1) >> 1;

        int yoff = (params[1] - xFixed1) >> 1;

        params += 2;
        for (i = 0; i < width; ++i) {
            if (!v.vector[2]) {
                buffer[i] = 0;
            }
            else {
                int x1, x2, y1, y2, x, y;

                INT32 srtot, sgtot, sbtot, satot;

                xFixed *p = params;

                if (!affine) {
                    xFixed_48_16 tmp;

                    tmp =
                        ((xFixed_48_16) v.vector[0] << 16) / v.vector[2] - xoff;
                    x1 = xFixedToInt(tmp);
                    tmp =
                        ((xFixed_48_16) v.vector[1] << 16) / v.vector[2] - yoff;
                    y1 = xFixedToInt(tmp);
                }
                else {
                    x1 = xFixedToInt(v.vector[0] - xoff);
                    y1 = xFixedToInt(v.vector[1] - yoff);
                }
                x2 = x1 + cwidth;
                y2 = y1 + cheight;

                srtot = sgtot = sbtot = satot = 0;

                for (y = y1; y < y2; y++) {
                    int ty =
                        (pict->repeatType == RepeatNormal) ? MOD(y,
                                                                 pict->
                                                                 pDrawable->
                                                                 height) : y;
                    for (x = x1; x < x2; x++) {
                        if (*p) {
                            int tx =
                                (pict->repeatType == RepeatNormal) ? MOD(x,
                                                                         pict->
                                                                         pDrawable->
                                                                         width)
                                : x;
                            if (POINT_IN_REGION(pict->pCompositeClip, tx + dx, ty + dy,
                                 &box)) {
                                FbBits *b = bits + (ty + dy) * stride;

                                CARD32 c = fetch(b, tx + dx, indexed);

                                srtot += Red(c) * *p;
                                sgtot += Green(c) * *p;
                                sbtot += Blue(c) * *p;
                                satot += Alpha(c) * *p;
                            }
                        }
                        p++;
                    }
                }

                satot >>= 16;
                srtot >>= 16;
                sgtot >>= 16;
                sbtot >>= 16;

                if (satot < 0)
                    satot = 0;
                else if (satot > 0xff)
                    satot = 0xff;
                if (srtot < 0)
                    srtot = 0;
                else if (srtot > 0xff)
                    srtot = 0xff;
                if (sgtot < 0)
                    sgtot = 0;
                else if (sgtot > 0xff)
                    sgtot = 0xff;
                if (sbtot < 0)
                    sbtot = 0;
                else if (sbtot > 0xff)
                    sbtot = 0xff;

                buffer[i] = ((satot << 24) |
                             (srtot << 16) | (sgtot << 8) | (sbtot));
            }
            v.vector[0] += unit.vector[0];
            v.vector[1] += unit.vector[1];
            v.vector[2] += unit.vector[2];
        }
    }
}

static void
fbFetchExternalAlpha(PicturePtr pict, int x, int y, int width, CARD32 *buffer)
{
    int i;

    CARD32 _alpha_buffer[SCANLINE_BUFFER_LENGTH];

    CARD32 *alpha_buffer = _alpha_buffer;

    if (!pict->alphaMap) {
        fbFetchTransformed(pict, x, y, width, buffer);
        return;
    }
    if (width > SCANLINE_BUFFER_LENGTH)
        alpha_buffer = malloc(width * sizeof(CARD32));

    fbFetchTransformed(pict, x, y, width, buffer);
    fbFetchTransformed(pict->alphaMap, x - pict->alphaOrigin.x,
                       y - pict->alphaOrigin.y, width, alpha_buffer);
    for (i = 0; i < width; ++i) {
        int a = alpha_buffer[i] >> 24;

        buffer[i] = (a << 24)
            | (div_255(Red(buffer[i]) * a) << 16)
            | (div_255(Green(buffer[i]) * a) << 8)
            | (div_255(Blue(buffer[i]) * a));
    }

    if (alpha_buffer != _alpha_buffer)
        free(alpha_buffer);
}

static void
fbStore(PicturePtr pict, int x, int y, int width, CARD32 *buffer)
{
    FbBits *bits;

    FbStride stride;

    int bpp;

    int xoff, yoff;

    storeProc store = storeProcForPicture(pict);

    miIndexedPtr indexed = (miIndexedPtr) pict->pFormat->index.devPrivate;

    fbGetDrawable(pict->pDrawable, bits, stride, bpp, xoff, yoff);
    x += xoff;
    y += yoff;

    bits += y * stride;
    store(bits, buffer, x, width, indexed);
}

static void
fbStoreExternalAlpha(PicturePtr pict, int x, int y, int width, CARD32 *buffer)
{
    FbBits *bits, *alpha_bits;

    FbStride stride, astride;

    int bpp, abpp;

    int xoff, yoff;

    int ax, ay;

    storeProc store;

    storeProc astore;

    miIndexedPtr indexed = (miIndexedPtr) pict->pFormat->index.devPrivate;

    miIndexedPtr aindexed;

    if (!pict->alphaMap) {
        fbStore(pict, x, y, width, buffer);
        return;
    }

    store = storeProcForPicture(pict);
    astore = storeProcForPicture(pict->alphaMap);
    aindexed = (miIndexedPtr) pict->alphaMap->pFormat->index.devPrivate;

    ax = x;
    ay = y;

    fbGetDrawable(pict->pDrawable, bits, stride, bpp, xoff, yoff);
    x += xoff;
    y += yoff;
    fbGetDrawable(pict->alphaMap->pDrawable, alpha_bits, astride, abpp, xoff,
                  yoff);
    ax += xoff;
    ay += yoff;

    bits += y * stride;
    alpha_bits += (ay - pict->alphaOrigin.y) * astride;

    store(bits, buffer, x, width, indexed);
    astore(alpha_bits, buffer, ax - pict->alphaOrigin.x, width, aindexed);
}

typedef void (*scanStoreProc) (PicturePtr, int, int, int, CARD32 *);

typedef void (*scanFetchProc) (PicturePtr, int, int, int, CARD32 *);

static void
fbCompositeRect(const FbComposeData * data, CARD32 *scanline_buffer)
{
    CARD32 *src_buffer = scanline_buffer;

    CARD32 *dest_buffer = src_buffer + data->width;

    int i;

    scanStoreProc store;

    scanFetchProc fetchSrc = NULL, fetchMask = NULL, fetchDest = NULL;

    if (data->op == PictOpClear)
        fetchSrc = NULL;
    else if (!data->src->pDrawable) {
        if (data->src->pSourcePict)
            fetchSrc = fbFetchSourcePict;
    }
    else if (data->src->alphaMap)
        fetchSrc = fbFetchExternalAlpha;
    else if (data->src->repeatType == RepeatNormal &&
             data->src->pDrawable->width == 1 &&
             data->src->pDrawable->height == 1)
        fetchSrc = fbFetchSolid;
    else if (!data->src->transform &&
             data->src->filter != PictFilterConvolution)
        fetchSrc = fbFetch;
    else
        fetchSrc = fbFetchTransformed;

    if (data->mask && data->op != PictOpClear) {
        if (!data->mask->pDrawable) {
            if (data->mask->pSourcePict)
                fetchMask = fbFetchSourcePict;
        }
        else if (data->mask->alphaMap)
            fetchMask = fbFetchExternalAlpha;
        else if (data->mask->repeatType == RepeatNormal
                 && data->mask->pDrawable->width == 1 &&
                 data->mask->pDrawable->height == 1)
            fetchMask = fbFetchSolid;
        else if (!data->mask->transform &&
                 data->mask->filter != PictFilterConvolution)
            fetchMask = fbFetch;
        else
            fetchMask = fbFetchTransformed;
    }
    else {
        fetchMask = NULL;
    }

    if (data->dest->alphaMap) {
        fetchDest = fbFetchExternalAlpha;
        store = fbStoreExternalAlpha;
    }
    else {
        fetchDest = fbFetch;
        store = fbStore;
    }
    if (data->op == PictOpClear || data->op == PictOpSrc)
        fetchDest = NULL;

    if (fetchSrc && fetchMask && data->mask && data->mask->componentAlpha &&
        PICT_FORMAT_RGB(data->mask->format)) {
        CARD32 *mask_buffer = dest_buffer + data->width;

        CombineFuncC compose = composeFunctions.combineC[data->op];

        if (!compose)
            return;

        for (i = 0; i < data->height; ++i) {
            /* fill first half of scanline with source */
            fetchSrc(data->src, data->xSrc, data->ySrc + i, data->width,
                     src_buffer);
            fetchMask(data->mask, data->xMask, data->yMask + i, data->width,
                      mask_buffer);

            /* fill dest into second half of scanline */
            if (fetchDest)
                fetchDest(data->dest, data->xDest, data->yDest + i, data->width,
                          dest_buffer);

            /* blend */
            compose(dest_buffer, src_buffer, mask_buffer, data->width);

            /* write back */
            store(data->dest, data->xDest, data->yDest + i, data->width,
                  dest_buffer);
        }
    }
    else {

        CombineFuncU compose = composeFunctions.combineU[data->op];

        if (!compose)
            return;

        if (fetchSrc == fbFetchSolid &&
            (!fetchMask || fetchMask == fbFetchSolid)) {
            fetchSrc(data->src, data->xSrc, data->ySrc, data->width,
                     src_buffer);
            if (fetchMask) {
                fetchMask(data->mask, data->xMask, data->yMask, data->width,
                          dest_buffer);
                composeFunctions.combineMaskU(src_buffer, dest_buffer,
                                              data->width);
            }
            fetchSrc = NULL;
            fetchMask = NULL;
        }

        for (i = 0; i < data->height; ++i) {
            /* fill first half of scanline with source */
            if (fetchSrc) {
                fetchSrc(data->src, data->xSrc, data->ySrc + i, data->width,
                         src_buffer);

                /* add in mask */
                if (fetchMask) {
                    fetchMask(data->mask, data->xMask, data->yMask + i,
                              data->width, dest_buffer);
                    composeFunctions.combineMaskU(src_buffer, dest_buffer,
                                                  data->width);
                }
            }

            /* fill dest into second half of scanline */
            if (fetchDest)
                fetchDest(data->dest, data->xDest, data->yDest + i, data->width,
                          dest_buffer);

            /* blend */
            compose(dest_buffer, src_buffer, data->width);

            /* write back */
            store(data->dest, data->xDest, data->yDest + i, data->width,
                  dest_buffer);
        }
    }
}

void
fbCompositeGeneral(CARD8 op,
                   PicturePtr pSrc,
                   PicturePtr pMask,
                   PicturePtr pDst,
                   INT16 xSrc,
                   INT16 ySrc,
                   INT16 xMask,
                   INT16 yMask,
                   INT16 xDst, INT16 yDst, CARD16 width, CARD16 height)
{
    RegionRec region;

    int n;

    BoxPtr pbox;

    Bool srcRepeat = FALSE;

    Bool maskRepeat = FALSE;

    int w, h;

    CARD32 _scanline_buffer[SCANLINE_BUFFER_LENGTH * 3];

    CARD32 *scanline_buffer = _scanline_buffer;

    FbComposeData compose_data;

    if (pSrc->pDrawable)
        srcRepeat = pSrc->repeatType == RepeatNormal && !pSrc->transform
            && (pSrc->pDrawable->width != 1 || pSrc->pDrawable->height != 1);

    if (pMask && pMask->pDrawable)
        maskRepeat = pMask->repeatType == RepeatNormal && !pMask->transform
            && (pMask->pDrawable->width != 1 || pMask->pDrawable->height != 1);

    if (op == PictOpOver && !pMask && !pSrc->transform &&
        !PICT_FORMAT_A(pSrc->format) && !pSrc->alphaMap)
        op = PictOpSrc;

    if (!miComputeCompositeRegion(&region,
                                  pSrc,
                                  pMask,
                                  pDst,
                                  xSrc,
                                  ySrc,
                                  xMask, yMask, xDst, yDst, width, height))
        return;

    compose_data.op = op;
    compose_data.src = pSrc;
    compose_data.mask = pMask;
    compose_data.dest = pDst;
    if (width > SCANLINE_BUFFER_LENGTH)
        scanline_buffer = malloc(width * 3 * sizeof(CARD32));

    n = REGION_NUM_RECTS(&region);
    pbox = REGION_RECTS(&region);
    while (n--) {
        h = pbox->y2 - pbox->y1;
        compose_data.ySrc = pbox->y1 - yDst + ySrc;
        compose_data.yMask = pbox->y1 - yDst + yMask;
        compose_data.yDest = pbox->y1;
        while (h) {
            compose_data.height = h;
            w = pbox->x2 - pbox->x1;
            compose_data.xSrc = pbox->x1 - xDst + xSrc;
            compose_data.xMask = pbox->x1 - xDst + xMask;
            compose_data.xDest = pbox->x1;
            if (maskRepeat) {
                compose_data.yMask =
                    mod(compose_data.yMask, pMask->pDrawable->height);
                if (compose_data.height >
                    pMask->pDrawable->height - compose_data.yMask)
                    compose_data.height =
                        pMask->pDrawable->height - compose_data.yMask;
            }
            if (srcRepeat) {
                compose_data.ySrc =
                    mod(compose_data.ySrc, pSrc->pDrawable->height);
                if (compose_data.height >
                    pSrc->pDrawable->height - compose_data.ySrc)
                    compose_data.height =
                        pSrc->pDrawable->height - compose_data.ySrc;
            }
            while (w) {
                compose_data.width = w;
                if (maskRepeat) {
                    compose_data.xMask =
                        mod(compose_data.xMask, pMask->pDrawable->width);
                    if (compose_data.width >
                        pMask->pDrawable->width - compose_data.xMask)
                        compose_data.width =
                            pMask->pDrawable->width - compose_data.xMask;
                }
                if (srcRepeat) {
                    compose_data.xSrc =
                        mod(compose_data.xSrc, pSrc->pDrawable->width);
                    if (compose_data.width >
                        pSrc->pDrawable->width - compose_data.xSrc)
                        compose_data.width =
                            pSrc->pDrawable->width - compose_data.xSrc;
                }
                fbCompositeRect(&compose_data, scanline_buffer);
                w -= compose_data.width;
                compose_data.xSrc += compose_data.width;
                compose_data.xMask += compose_data.width;
                compose_data.xDest += compose_data.width;
            }
            h -= compose_data.height;
            compose_data.ySrc += compose_data.height;
            compose_data.yMask += compose_data.height;
            compose_data.yDest += compose_data.height;
        }
        pbox++;
    }
    REGION_UNINIT(&region);

    if (scanline_buffer != _scanline_buffer)
        free(scanline_buffer);
}

